@@ -77,7 +78,7 @@ class ComTest(TestCase):
)
def test_info_msg(self):
- response = self.client.post(
+ self.client.post(
reverse("com:info_edit"),
{
"info_msg": """
@@ -86,7 +87,6 @@ class ComTest(TestCase):
},
)
r = self.client.get(reverse("core:index"))
- self.assertTrue(r.status_code == 200)
self.assertContains(
r,
"""
@@ -94,7 +94,7 @@ class ComTest(TestCase):
)
def test_birthday_non_subscribed_user(self):
- self.client.login(username="guy", password="plop")
+ self.client.force_login(User.objects.get(username="guy"))
response = self.client.get(reverse("core:index"))
self.assertContains(
response,
@@ -123,13 +123,13 @@ class SithTest(TestCase):
sith: Sith = Sith.objects.first()
com_admin = User.objects.get(username="comunity")
- self.assertTrue(sith.is_owned_by(com_admin))
+ assert sith.is_owned_by(com_admin)
anonymous = AnonymousUser()
- self.assertFalse(sith.is_owned_by(anonymous))
+ assert not sith.is_owned_by(anonymous)
sli = User.objects.get(username="sli")
- self.assertFalse(sith.is_owned_by(sli))
+ assert not sith.is_owned_by(sli)
class NewsTest(TestCase):
@@ -154,10 +154,10 @@ class NewsTest(TestCase):
or by their author but nobody else
"""
- self.assertTrue(self.new.is_owned_by(self.com_admin))
- self.assertTrue(self.new.is_owned_by(self.author))
- self.assertFalse(self.new.is_owned_by(self.anonymous))
- self.assertFalse(self.new.is_owned_by(self.sli))
+ assert self.new.is_owned_by(self.com_admin)
+ assert self.new.is_owned_by(self.author)
+ assert not self.new.is_owned_by(self.anonymous)
+ assert not self.new.is_owned_by(self.sli)
def test_news_viewer(self):
"""
@@ -165,26 +165,26 @@ class NewsTest(TestCase):
and not moderated news only by com admins
"""
# by default a news isn't moderated
- self.assertTrue(self.new.can_be_viewed_by(self.com_admin))
- self.assertFalse(self.new.can_be_viewed_by(self.sli))
- self.assertFalse(self.new.can_be_viewed_by(self.anonymous))
- self.assertFalse(self.new.can_be_viewed_by(self.author))
+ assert self.new.can_be_viewed_by(self.com_admin)
+ assert not self.new.can_be_viewed_by(self.sli)
+ assert not self.new.can_be_viewed_by(self.anonymous)
+ assert not self.new.can_be_viewed_by(self.author)
self.new.is_moderated = True
self.new.save()
- self.assertTrue(self.new.can_be_viewed_by(self.com_admin))
- self.assertTrue(self.new.can_be_viewed_by(self.sli))
- self.assertTrue(self.new.can_be_viewed_by(self.anonymous))
- self.assertTrue(self.new.can_be_viewed_by(self.author))
+ assert self.new.can_be_viewed_by(self.com_admin)
+ assert self.new.can_be_viewed_by(self.sli)
+ assert self.new.can_be_viewed_by(self.anonymous)
+ assert self.new.can_be_viewed_by(self.author)
def test_news_editor(self):
"""
Test that only com admins can edit news
"""
- self.assertTrue(self.new.can_be_edited_by(self.com_admin))
- self.assertFalse(self.new.can_be_edited_by(self.sli))
- self.assertFalse(self.new.can_be_edited_by(self.anonymous))
- self.assertFalse(self.new.can_be_edited_by(self.author))
+ assert self.new.can_be_edited_by(self.com_admin)
+ assert not self.new.can_be_edited_by(self.sli)
+ assert not self.new.can_be_edited_by(self.anonymous)
+ assert not self.new.can_be_edited_by(self.author)
class WeekmailArticleTest(TestCase):
@@ -207,10 +207,10 @@ class WeekmailArticleTest(TestCase):
"""
Test that weekmails are owned only by com admins
"""
- self.assertTrue(self.article.is_owned_by(self.com_admin))
- self.assertFalse(self.article.is_owned_by(self.author))
- self.assertFalse(self.article.is_owned_by(self.anonymous))
- self.assertFalse(self.article.is_owned_by(self.sli))
+ assert self.article.is_owned_by(self.com_admin)
+ assert not self.article.is_owned_by(self.author)
+ assert not self.article.is_owned_by(self.anonymous)
+ assert not self.article.is_owned_by(self.sli)
class PosterTest(TestCase):
@@ -233,8 +233,8 @@ class PosterTest(TestCase):
"""
Test that poster are owned by com admins and board members in clubs
"""
- self.assertTrue(self.poster.is_owned_by(self.com_admin))
- self.assertFalse(self.poster.is_owned_by(self.anonymous))
+ assert self.poster.is_owned_by(self.com_admin)
+ assert not self.poster.is_owned_by(self.anonymous)
- self.assertFalse(self.poster.is_owned_by(self.susbcriber))
- self.assertTrue(self.poster.is_owned_by(self.sli))
+ assert not self.poster.is_owned_by(self.susbcriber)
+ assert self.poster.is_owned_by(self.sli)
diff --git a/com/views.py b/com/views.py
index f8d0119b..774d1c5f 100644
--- a/com/views.py
+++ b/com/views.py
@@ -23,38 +23,35 @@
#
#
-from django.shortcuts import redirect, get_object_or_404
-from django.http import HttpResponseRedirect
-from django.views.generic import ListView, DetailView, View
-from django.views.generic.edit import UpdateView, CreateView, DeleteView
-from django.views.generic.detail import SingleObjectMixin
-from django.utils.translation import gettext_lazy as _
-from django.urls import reverse, reverse_lazy
-from django.core.exceptions import ValidationError
-from django.utils import timezone
-from django.conf import settings
-from django.db.models import Max
-from django.forms.models import modelform_factory
-from django.core.exceptions import PermissionDenied
-from django import forms
-
from datetime import timedelta
from smtplib import SMTPRecipientsRefused
-from com.models import Sith, News, NewsDate, Weekmail, WeekmailArticle, Screen, Poster
+from django import forms
+from django.conf import settings
+from django.core.exceptions import PermissionDenied, ValidationError
+from django.db.models import Max
+from django.forms.models import modelform_factory
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404, redirect
+from django.urls import reverse, reverse_lazy
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, ListView, View
+from django.views.generic.detail import SingleObjectMixin
+from django.views.generic.edit import CreateView, DeleteView, UpdateView
+
+from club.models import Club, Mailing
+from com.models import News, NewsDate, Poster, Screen, Sith, Weekmail, WeekmailArticle
+from core.models import Notification, RealGroup, User
from core.views import (
- CanViewMixin,
+ CanCreateMixin,
CanEditMixin,
CanEditPropMixin,
- TabedViewMixin,
- CanCreateMixin,
+ CanViewMixin,
QuickNotifMixin,
+ TabedViewMixin,
)
-from core.views.forms import SelectDateTime, MarkdownInput
-from core.models import Notification, RealGroup, User
-from club.models import Club, Mailing
-from core.views.forms import TzAwareDateTimeField
-
+from core.views.forms import MarkdownInput, TzAwareDateTimeField
# Sith object
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 00000000..e209b26d
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,14 @@
+import pytest
+from django.core.management import call_command
+from django.utils.translation import activate
+
+
+@pytest.fixture(scope="session")
+def django_db_setup(django_db_setup, django_db_blocker):
+ with django_db_blocker.unblock():
+ call_command("populate")
+
+
+@pytest.fixture(scope="session", autouse=True)
+def set_default_language():
+ activate("fr")
diff --git a/core/admin.py b/core/admin.py
index 33ce50e4..8b202a30 100644
--- a/core/admin.py
+++ b/core/admin.py
@@ -14,12 +14,12 @@
#
#
-from django.contrib import admin
from ajax_select import make_ajax_form
-from core.models import User, Page, RealGroup, MetaGroup, SithFile
+from django.contrib import admin
from django.contrib.auth.models import Group as AuthGroup
from haystack.admin import SearchModelAdmin
+from core.models import MetaGroup, Page, RealGroup, SithFile, User
admin.site.unregister(AuthGroup)
admin.site.register(MetaGroup)
diff --git a/core/apps.py b/core/apps.py
index cd131f57..872f34ae 100644
--- a/core/apps.py
+++ b/core/apps.py
@@ -34,8 +34,8 @@ class SithConfig(AppConfig):
verbose_name = "Core app of the Sith"
def ready(self):
+ import core.signals # noqa F401
from forum.models import Forum
- import core.signals
cache.clear()
diff --git a/core/converters.py b/core/converters.py
index cb7bc95b..b681564d 100644
--- a/core/converters.py
+++ b/core/converters.py
@@ -1,6 +1,3 @@
-from core.models import Page
-
-
class FourDigitYearConverter:
regex = "[0-9]{4}"
diff --git a/core/lookups.py b/core/lookups.py
index f245442b..15205194 100644
--- a/core/lookups.py
+++ b/core/lookups.py
@@ -14,15 +14,14 @@
#
#
+from ajax_select import LookupChannel, register
from django.core.exceptions import PermissionDenied
-from ajax_select import register, LookupChannel
-from core.views.site import search_user
-from core.models import User, Group, SithFile
-from club.models import Club
-from counter.models import Product, Counter, Customer
from accounting.models import ClubAccount, Company
-from eboutic.models import BasketItem
+from club.models import Club
+from core.models import Group, SithFile, User
+from core.views.site import search_user
+from counter.models import Counter, Customer, Product
def check_token(request):
diff --git a/core/management/commands/check_fs.py b/core/management/commands/check_fs.py
index 7ebc076d..a6fc9597 100644
--- a/core/management/commands/check_fs.py
+++ b/core/management/commands/check_fs.py
@@ -23,8 +23,8 @@
#
import os
+
from django.core.management.base import BaseCommand
-from django.core.management import call_command
from core.models import SithFile
diff --git a/core/management/commands/compilemessages.py b/core/management/commands/compilemessages.py
index b3c336bc..87f1b2de 100644
--- a/core/management/commands/compilemessages.py
+++ b/core/management/commands/compilemessages.py
@@ -25,6 +25,7 @@
import os
+
from django.core.management.commands import compilemessages
diff --git a/core/management/commands/compilestatic.py b/core/management/commands/compilestatic.py
index 31e5c13e..f1268c23 100644
--- a/core/management/commands/compilestatic.py
+++ b/core/management/commands/compilestatic.py
@@ -24,9 +24,10 @@
#
import os
+
import sass
-from django.core.management.base import BaseCommand
from django.conf import settings
+from django.core.management.base import BaseCommand
class Command(BaseCommand):
diff --git a/core/management/commands/documentation.py b/core/management/commands/documentation.py
index 79f2e0d8..bcaa0664 100644
--- a/core/management/commands/documentation.py
+++ b/core/management/commands/documentation.py
@@ -24,10 +24,9 @@
#
import os
-import sys
import signal
-
-from http.server import test, CGIHTTPRequestHandler
+import sys
+from http.server import CGIHTTPRequestHandler, test
from django.core.management.base import BaseCommand
from django.utils import autoreload
diff --git a/core/management/commands/install_xapian.py b/core/management/commands/install_xapian.py
new file mode 100644
index 00000000..9181c151
--- /dev/null
+++ b/core/management/commands/install_xapian.py
@@ -0,0 +1,68 @@
+# -*- coding:utf-8 -*
+#
+# Copyright 2024 © AE UTBM
+# ae@utbm.fr / ae.info@utbm.fr
+#
+# This file is part of the website of the UTBM Student Association (AE UTBM),
+# https://ae.utbm.fr.
+#
+# You can find the source code of the website at https://github.com/ae-utbm/sith3
+#
+# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
+# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
+# OR WITHIN THE LOCAL FILE "LICENSE"
+#
+#
+
+import os
+import subprocess
+from pathlib import Path
+
+import tomli
+from django.core.management.base import BaseCommand, CommandParser
+
+
+class Command(BaseCommand):
+ help = "Install xapian"
+
+ def add_arguments(self, parser: CommandParser):
+ parser.add_argument(
+ "-f",
+ "--force",
+ action="store_true",
+ help="Force installation even if already installed",
+ )
+
+ def _current_version(self) -> str | None:
+ try:
+ import xapian
+ except ImportError:
+ return None
+ return xapian.version_string()
+
+ def _desired_version(self) -> str:
+ with open(
+ Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb"
+ ) as f:
+ pyproject = tomli.load(f)
+ return pyproject["tool"]["xapian"]["version"]
+
+ def handle(self, force: bool, *args, **options):
+ if not os.environ.get("VIRTUAL_ENV", None):
+ print("No virtual environment detected, this command can't be used")
+ return
+
+ desired = self._desired_version()
+ if desired == self._current_version():
+ if not force:
+ print(
+ f"Version {desired} is already installed, use --force to re-install"
+ )
+ return
+ print(f"Version {desired} is already installed, re-installing")
+ print(f"Installing xapian version {desired} at {os.environ['VIRTUAL_ENV']}")
+ subprocess.run(
+ [str(Path(__file__).parent / "install_xapian.sh"), desired],
+ env=dict(os.environ),
+ ).check_returncode()
+ print("Installation success")
diff --git a/core/management/commands/install_xapian.sh b/core/management/commands/install_xapian.sh
new file mode 100755
index 00000000..ae183f6e
--- /dev/null
+++ b/core/management/commands/install_xapian.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+# Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd
+# first argument of the script is Xapian version (e.g. 1.2.19)
+VERSION=$1
+
+# Cleanup env vars for auto discovery mechanism
+export CPATH=
+export LIBRARY_PATH=
+export CFLAGS=
+export LDFLAGS=
+export CCFLAGS=
+export CXXFLAGS=
+export CPPFLAGS=
+
+# prepare
+rm -rf "$VIRTUAL_ENV/packages"
+mkdir -p "$VIRTUAL_ENV/packages" && cd "$VIRTUAL_ENV/packages" || exit 1
+
+CORE=xapian-core-$VERSION
+BINDINGS=xapian-bindings-$VERSION
+
+# download
+echo "Downloading source..."
+curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz"
+curl -O "https://oligarchy.co.uk/xapian/$VERSION/${BINDINGS}.tar.xz"
+
+# extract
+echo "Extracting source..."
+tar xf "${CORE}.tar.xz"
+tar xf "${BINDINGS}.tar.xz"
+
+# install
+echo "Installing Xapian-core..."
+cd "$VIRTUAL_ENV/packages/${CORE}" || exit 1
+./configure --prefix="$VIRTUAL_ENV" && make && make install
+
+PYTHON_FLAG=--with-python3
+
+echo "Installing Xapian-bindings..."
+cd "$VIRTUAL_ENV/packages/${BINDINGS}" || exit 1
+./configure --prefix="$VIRTUAL_ENV" $PYTHON_FLAG XAPIAN_CONFIG="$VIRTUAL_ENV/bin/xapian-config" && make && make install
+
+# clean
+rm -rf "$VIRTUAL_ENV/packages"
+
+# test
+python -c "import xapian"
diff --git a/core/management/commands/markdown.py b/core/management/commands/markdown.py
index 1b5a6855..35941ec4 100644
--- a/core/management/commands/markdown.py
+++ b/core/management/commands/markdown.py
@@ -23,6 +23,7 @@
#
import os
+
from django.core.management.base import BaseCommand
from core.markdown import markdown
diff --git a/core/management/commands/populate.py b/core/management/commands/populate.py
index c78de401..d42d9987 100644
--- a/core/management/commands/populate.py
+++ b/core/management/commands/populate.py
@@ -24,38 +24,37 @@
import os
from datetime import date, datetime, timedelta
-from io import StringIO, BytesIO
+from io import BytesIO, StringIO
from pathlib import Path
-from django.contrib.auth.models import Permission
-from django.core.management.base import BaseCommand
-from django.core.management import call_command
from django.conf import settings
-from django.db import connection
+from django.contrib.auth.models import Permission
from django.contrib.sites.models import Site
+from django.core.management import call_command
+from django.core.management.base import BaseCommand
+from django.db import connection
from django.utils import timezone
-
from PIL import Image
-from core.models import Group, User, Page, PageRev, SithFile
from accounting.models import (
- GeneralJournal,
+ AccountingType,
BankAccount,
ClubAccount,
- Operation,
- AccountingType,
- SimplifiedAccountingType,
Company,
+ GeneralJournal,
+ Operation,
+ SimplifiedAccountingType,
)
-from core.utils import resize_image
from club.models import Club, Membership
-from subscription.models import Subscription
-from counter.models import Customer, ProductType, Product, Counter, Selling, StudentCard
-from com.models import Sith, Weekmail, News, NewsDate
-from election.models import Election, Role, Candidature, ElectionList
+from com.models import News, NewsDate, Sith, Weekmail
+from core.models import Group, Page, PageRev, SithFile, User
+from core.utils import resize_image
+from counter.models import Counter, Customer, Product, ProductType, Selling, StudentCard
+from election.models import Candidature, Election, ElectionList, Role
from forum.models import Forum, ForumTopic
from pedagogy.models import UV
-from sas.models import Album, Picture, PeoplePictureRelation
+from sas.models import Album, PeoplePictureRelation, Picture
+from subscription.models import Subscription
class Command(BaseCommand):
@@ -1099,8 +1098,10 @@ Welcome to the wiki page!
n = News(
title="Repas barman",
summary="Enjoy la fin du semestre!",
- content="Viens donc t'enjailler avec les autres barmans aux "
- "frais du BdF! \o/",
+ content=(
+ "Viens donc t'enjailler avec les autres barmans aux "
+ "frais du BdF! \\o/"
+ ),
type="EVENT",
club=bar_club,
author=subscriber,
diff --git a/core/management/commands/repair_fs.py b/core/management/commands/repair_fs.py
index 055dbc9b..21408084 100644
--- a/core/management/commands/repair_fs.py
+++ b/core/management/commands/repair_fs.py
@@ -23,8 +23,8 @@
#
import os
+
from django.core.management.base import BaseCommand
-from django.core.management import call_command
from core.models import SithFile
diff --git a/core/management/commands/setup.py b/core/management/commands/setup.py
index cc0ee1ca..5c91e1e6 100644
--- a/core/management/commands/setup.py
+++ b/core/management/commands/setup.py
@@ -15,8 +15,9 @@
#
import os
-from django.core.management.base import BaseCommand
+
from django.core.management import call_command
+from django.core.management.base import BaseCommand
class Command(BaseCommand):
diff --git a/core/markdown.py b/core/markdown.py
index 72b1cd02..0abe1954 100644
--- a/core/markdown.py
+++ b/core/markdown.py
@@ -16,8 +16,9 @@
import os
import re
-from mistune import Renderer, InlineGrammar, InlineLexer, Markdown, escape, escape_link
+
from django.urls import reverse
+from mistune import InlineGrammar, InlineLexer, Markdown, Renderer, escape, escape_link
class SithRenderer(Renderer):
diff --git a/core/middleware.py b/core/middleware.py
index 39afa266..ddc6dea3 100644
--- a/core/middleware.py
+++ b/core/middleware.py
@@ -16,12 +16,13 @@
import importlib
import threading
+
from django.conf import settings
-from django.utils.functional import SimpleLazyObject
from django.contrib.auth import get_user
from django.contrib.auth.middleware import (
AuthenticationMiddleware as DjangoAuthenticationMiddleware,
)
+from django.utils.functional import SimpleLazyObject
module, klass = settings.AUTH_ANONYMOUS_MODEL.rsplit(".", 1)
AnonymousUser = getattr(importlib.import_module(module), klass)
diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py
index 7474e93a..57050f26 100644
--- a/core/migrations/0001_initial.py
+++ b/core/migrations/0001_initial.py
@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.contrib.auth.models
-import django.db.models.deletion
import django.core.validators
-import core.models
+import django.db.models.deletion
import phonenumber_field.modelfields
from django.conf import settings
-import django.db.models.deletion
+from django.db import migrations, models
+
+import core.models
class Migration(migrations.Migration):
diff --git a/core/migrations/0003_auto_20160902_1914.py b/core/migrations/0003_auto_20160902_1914.py
index b39d9838..65f11d3a 100644
--- a/core/migrations/0003_auto_20160902_1914.py
+++ b/core/migrations/0003_auto_20160902_1914.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.core.validators
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0004_user_godfathers.py b/core/migrations/0004_user_godfathers.py
index d068cfc7..d4066cc5 100644
--- a/core/migrations/0004_user_godfathers.py
+++ b/core/migrations/0004_user_godfathers.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0005_auto_20161105_1035.py b/core/migrations/0005_auto_20161105_1035.py
index 7d40b163..6f7c487f 100644
--- a/core/migrations/0005_auto_20161105_1035.py
+++ b/core/migrations/0005_auto_20161105_1035.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0009_auto_20161120_1155.py b/core/migrations/0009_auto_20161120_1155.py
index c017706a..aafb2c54 100644
--- a/core/migrations/0009_auto_20161120_1155.py
+++ b/core/migrations/0009_auto_20161120_1155.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
+
import core.models
diff --git a/core/migrations/0011_auto_20161124_0848.py b/core/migrations/0011_auto_20161124_0848.py
index b3ea7f4a..6475189e 100644
--- a/core/migrations/0011_auto_20161124_0848.py
+++ b/core/migrations/0011_auto_20161124_0848.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.utils.timezone
+from django.db import migrations, models
+
import core.models
diff --git a/core/migrations/0012_notification.py b/core/migrations/0012_notification.py
index 360fbcb8..245a38e3 100644
--- a/core/migrations/0012_notification.py
+++ b/core/migrations/0012_notification.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
-import django.utils.timezone
import django.db.models.deletion
+import django.utils.timezone
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0015_sithfile_moderator.py b/core/migrations/0015_sithfile_moderator.py
index 20d1512f..4e2a438d 100644
--- a/core/migrations/0015_sithfile_moderator.py
+++ b/core/migrations/0015_sithfile_moderator.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0016_auto_20161212_1922.py b/core/migrations/0016_auto_20161212_1922.py
index bde89b82..72432467 100644
--- a/core/migrations/0016_auto_20161212_1922.py
+++ b/core/migrations/0016_auto_20161212_1922.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0020_auto_20170324_0917.py b/core/migrations/0020_auto_20170324_0917.py
index c026483a..219be979 100644
--- a/core/migrations/0020_auto_20170324_0917.py
+++ b/core/migrations/0020_auto_20170324_0917.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.core.validators
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0023_auto_20170902_1226.py b/core/migrations/0023_auto_20170902_1226.py
index 2cdc4c85..9bfd01ed 100644
--- a/core/migrations/0023_auto_20170902_1226.py
+++ b/core/migrations/0023_auto_20170902_1226.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0025_auto_20170919_1521.py b/core/migrations/0025_auto_20170919_1521.py
index f37a829b..f7bd278c 100644
--- a/core/migrations/0025_auto_20170919_1521.py
+++ b/core/migrations/0025_auto_20170919_1521.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.core.validators
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0027_gift.py b/core/migrations/0027_gift.py
index 21bf8442..ca8ce67b 100644
--- a/core/migrations/0027_gift.py
+++ b/core/migrations/0027_gift.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
-import django.utils.timezone
import django.db.models.deletion
+import django.utils.timezone
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/core/migrations/0029_auto_20180426_2013.py b/core/migrations/0029_auto_20180426_2013.py
index eadfd558..c591f47b 100644
--- a/core/migrations/0029_auto_20180426_2013.py
+++ b/core/migrations/0029_auto_20180426_2013.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-import core.models
import django.db.models.deletion
+from django.db import migrations, models
+
+import core.models
class Migration(migrations.Migration):
diff --git a/core/migrations/0034_operationlog.py b/core/migrations/0034_operationlog.py
index df59cd6a..505e2332 100644
--- a/core/migrations/0034_operationlog.py
+++ b/core/migrations/0034_operationlog.py
@@ -1,8 +1,8 @@
# Generated by Django 2.2.6 on 2019-11-14 15:10
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
diff --git a/core/migrations/0038_alter_preferences_receive_weekmail.py b/core/migrations/0038_alter_preferences_receive_weekmail.py
new file mode 100644
index 00000000..4d029b9e
--- /dev/null
+++ b/core/migrations/0038_alter_preferences_receive_weekmail.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2 on 2024-06-26 09:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("core", "0037_auto_20211105_1708"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="preferences",
+ name="receive_weekmail",
+ field=models.BooleanField(
+ default=False, verbose_name="receive the Weekmail"
+ ),
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index c8a38426..4fba43c3 100644
--- a/core/models.py
+++ b/core/models.py
@@ -23,36 +23,39 @@
#
#
import importlib
-from typing import Union, Optional, List
+import os
+import unicodedata
+from datetime import date, timedelta
+from typing import List, Optional, Union
-from django.core.cache import cache
-from django.core.mail import send_mail
+from django.conf import settings
from django.contrib.auth.models import (
AbstractBaseUser,
UserManager,
- Group as AuthGroup,
- GroupManager as AuthGroupManager,
+)
+from django.contrib.auth.models import (
AnonymousUser as AuthAnonymousUser,
)
-from django.utils.translation import gettext_lazy as _
-from django.utils import timezone
-from django.core import validators
-from django.core.exceptions import ValidationError, PermissionDenied
-from django.urls import reverse
-from django.conf import settings
-from django.db import models, transaction
+from django.contrib.auth.models import (
+ Group as AuthGroup,
+)
+from django.contrib.auth.models import (
+ GroupManager as AuthGroupManager,
+)
from django.contrib.staticfiles.storage import staticfiles_storage
-from django.utils.html import escape
+from django.core import validators
+from django.core.cache import cache
+from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.mail import send_mail
+from django.db import models, transaction
+from django.urls import reverse
+from django.utils import timezone
from django.utils.functional import cached_property
-
-import os
-from core import utils
-
+from django.utils.html import escape
+from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField
-from datetime import timedelta, date
-
-import unicodedata
+from core import utils
class RealGroupManager(AuthGroupManager):
@@ -698,9 +701,11 @@ class User(AbstractBaseUser):
%s
""" % (
- self.profile_pict.get_download_url()
- if self.profile_pict
- else staticfiles_storage.url("core/img/unknown.jpg"),
+ (
+ self.profile_pict.get_download_url()
+ if self.profile_pict
+ else staticfiles_storage.url("core/img/unknown.jpg")
+ ),
_("Profile"),
escape(self.get_display_name()),
)
diff --git a/core/operations.py b/core/operations.py
index 29740292..10882f22 100644
--- a/core/operations.py
+++ b/core/operations.py
@@ -23,9 +23,9 @@
#
"""
- This page is useful for custom migration tricks.
- Sometimes, when you need to have a migration hack and you think it can be
- useful again, put it there, we never know if we might need the hack again.
+This page is useful for custom migration tricks.
+Sometimes, when you need to have a migration hack and you think it can be
+useful again, put it there, we never know if we might need the hack again.
"""
from django.db import connection, migrations
diff --git a/core/scss/finder.py b/core/scss/finder.py
index 3e25279f..0b62fab3 100644
--- a/core/scss/finder.py
+++ b/core/scss/finder.py
@@ -25,6 +25,7 @@
import os
from collections import OrderedDict
+
from django.conf import settings
from django.contrib.staticfiles.finders import FileSystemFinder
from django.core.files.storage import FileSystemStorage
diff --git a/core/scss/processor.py b/core/scss/processor.py
index 6aa334f2..add5e042 100644
--- a/core/scss/processor.py
+++ b/core/scss/processor.py
@@ -24,12 +24,14 @@
#
import os
-import sass
from urllib.parse import urljoin
-from django.utils.encoding import force_bytes, iri_to_uri
+
+import sass
+from django.conf import settings
from django.core.files.base import ContentFile
from django.templatetags.static import static
-from django.conf import settings
+from django.utils.encoding import force_bytes, iri_to_uri
+
from core.scss.storage import ScssFileStorage, find_file
diff --git a/core/search_indexes.py b/core/search_indexes.py
index b98dc67b..f2448adb 100644
--- a/core/search_indexes.py
+++ b/core/search_indexes.py
@@ -24,7 +24,6 @@
#
from django.db import models
-
from haystack import indexes, signals
from core.models import User
diff --git a/core/signals.py b/core/signals.py
index 7cda36c7..23e38599 100644
--- a/core/signals.py
+++ b/core/signals.py
@@ -8,10 +8,10 @@ from core.models import User
@receiver(m2m_changed, sender=User.groups.through, dispatch_uid="user_groups_changed")
def user_groups_changed(sender, instance: User, **kwargs):
"""
- Clear the cached clubs of the user
+ Clear the cached groups of the user
"""
# As a m2m relationship doesn't live within the model
# but rather on an intermediary table, there is no
# model method to override, meaning we must use
- # a signal to invalidate the cache when a user is removed from a club
- cache.delete(f"user_{instance.id}_groups")
+ # a signal to invalidate the cache when a user is removed from a group
+ cache.delete(f"user_{instance.pk}_groups")
diff --git a/core/templates/core/user_detail.jinja b/core/templates/core/user_detail.jinja
index 8a78ba48..b7651b88 100644
--- a/core/templates/core/user_detail.jinja
+++ b/core/templates/core/user_detail.jinja
@@ -162,8 +162,9 @@
diff --git a/core/templates/core/user_edit.jinja b/core/templates/core/user_edit.jinja
index f189d339..41b9dfb8 100644
--- a/core/templates/core/user_edit.jinja
+++ b/core/templates/core/user_edit.jinja
@@ -133,8 +133,9 @@
{%- elif user.is_root -%}
- {%- trans -%}Change user password{%-
- endtrans -%}
+
+ {%- trans -%}Change user password{%- endtrans -%}
+
{%- endif -%}
diff --git a/core/templatetags/renderer.py b/core/templatetags/renderer.py
index 2ee19a45..86bd6791 100644
--- a/core/templatetags/renderer.py
+++ b/core/templatetags/renderer.py
@@ -24,15 +24,15 @@
#
import datetime
-import phonenumbers
+import phonenumbers
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
from django.utils.translation import ngettext
-from core.scss.processor import ScssProcessor
from core.markdown import markdown as md
+from core.scss.processor import ScssProcessor
register = template.Library()
diff --git a/core/templatetags/search_helpers.py b/core/templatetags/search_helpers.py
index 537ff357..d38574df 100644
--- a/core/templatetags/search_helpers.py
+++ b/core/templatetags/search_helpers.py
@@ -1,6 +1,6 @@
-from django.template.exceptions import TemplateSyntaxError
from django import template
from django.template.defaultfilters import stringfilter
+from django.template.exceptions import TemplateSyntaxError
register = template.Library()
diff --git a/core/tests.py b/core/tests.py
index 61d497a5..733c0c38 100644
--- a/core/tests.py
+++ b/core/tests.py
@@ -18,10 +18,12 @@ import os
from datetime import date, timedelta
import freezegun
+import pytest
from django.core.cache import cache
-from django.test import Client, TestCase
+from django.test import TestCase
from django.urls import reverse
from django.utils.timezone import now
+from pytest_django.asserts import assertRedirects
from club.models import Membership
from core.markdown import markdown
@@ -29,270 +31,105 @@ from core.models import AnonymousUser, Group, Page, User
from core.utils import get_semester_code, get_start_of_semester
from sith import settings
-"""
-to run these tests :
- python3 manage.py test
-"""
+
+@pytest.mark.django_db
+class TestUserRegistration:
+ @pytest.fixture()
+ def valid_payload(self):
+ return {
+ "first_name": "this user does not exist (yet)",
+ "last_name": "this user does not exist (yet)",
+ "email": "i-dont-exist-yet@git.an",
+ "password1": "plop",
+ "password2": "plop",
+ "captcha_0": "dummy-value",
+ "captcha_1": "PASSED",
+ }
+
+ def test_register_user_form_ok(self, client, valid_payload):
+ """Should register a user correctly."""
+ response = client.post(reverse("core:register"), valid_payload)
+ assert response.status_code == 200
+ assert "TEST_REGISTER_USER_FORM_OK" in str(response.content)
+
+ @pytest.mark.parametrize(
+ "payload_edit",
+ [
+ {"password2": "not the same as password1"},
+ {"email": "not-an-email"},
+ {"first_name": ""},
+ {"last_name": ""},
+ {"captcha_1": "WRONG_CAPTCHA"},
+ ],
+ )
+ def test_register_user_form_fail(self, client, valid_payload, payload_edit):
+ """Should not register a user correctly."""
+ payload = valid_payload | payload_edit
+ response = client.post(reverse("core:register"), payload)
+ assert response.status_code == 200
+ assert "TEST_REGISTER_USER_FORM_FAIL" in str(response.content)
+
+ def test_register_user_form_fail_already_exists(self, client, valid_payload):
+ """Should not register a user correctly if it already exists."""
+ # create the user, then try to create it again
+ client.post(reverse("core:register"), valid_payload)
+ response = client.post(reverse("core:register"), valid_payload)
+ assert response.status_code == 200
+ assert "TEST_REGISTER_USER_FORM_FAIL" in str(response.content)
-class UserRegistrationTest(TestCase):
- @classmethod
- def setUpTestData(cls):
- User.objects.all().delete()
+@pytest.mark.django_db
+class TestUserLogin:
+ @pytest.fixture()
+ def user(self) -> User:
+ return User.objects.first()
- def test_register_user_form_ok(self):
- """
- Should register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "Carlier",
- "email": "guy@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_OK" in str(response.content))
-
- def test_register_user_form_fail_password(self):
- """
- Should not register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop2",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_register_user_form_fail_email(self):
- """
- Should not register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "Carlier",
- "email": "bibou.git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_register_user_form_fail_missing_name(self):
- """
- Should not register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_register_user_form_fail_missing_date_of_birth(self):
- """
- Should not register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_register_user_form_fail_missing_first_name(self):
- """
- Should not register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_register_user_form_fail_wrong_captcha(self):
- """
- Should not register a user correctly
- """
- c = Client()
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "Bibou",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "WRONG_CAPTCHA",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_register_user_form_fail_already_exists(self):
- """
- Should not register a user correctly
- """
- c = Client()
- c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- response = c.post(
- reverse("core:register"),
- {
- "first_name": "Bibou",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue("TEST_REGISTER_USER_FORM_FAIL" in str(response.content))
-
- def test_login_success(self):
- """
- Should login a user correctly
- """
- c = Client()
- c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
- )
- response = c.post(
- reverse("core:login"), {"username": "gcarlier", "password": "plop"}
- )
- self.assertTrue(response.status_code == 302)
- # self.assertTrue('Hello, world' in str(response.content))
-
- def test_login_fail(self):
+ def test_login_fail(self, client, user):
"""
Should not login a user correctly
"""
- c = Client()
- c.post(
- reverse("core:register"),
- {
- "first_name": "Guy",
- "last_name": "Carlier",
- "email": "bibou@git.an",
- "date_of_birth": "12/6/1942",
- "password1": "plop",
- "password2": "plop",
- "captcha_0": "dummy-value",
- "captcha_1": "PASSED",
- },
+
+ response = client.post(
+ reverse("core:login"),
+ {"username": user.username, "password": "wrong-password"},
)
- response = c.post(
- reverse("core:login"), {"username": "gcarlier", "password": "guy"}
- )
- self.assertTrue(response.status_code == 200)
- self.assertTrue(
- """
Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.
"""
- in str(response.content)
+ assert response.status_code == 200
+ assert (
+ '
Votre nom d\'utilisateur '
+ "et votre mot de passe ne correspondent pas. Merci de réessayer.
"
+ ) in str(response.content.decode())
+
+ def test_login_success(self, client, user):
+ """
+ Should login a user correctly
+ """
+ response = client.post(
+ reverse("core:login"), {"username": user.username, "password": "plop"}
)
+ assertRedirects(response, reverse("core:index"))
-class MarkdownTest(TestCase):
- def test_full_markdown_syntax(self):
- root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- with open(os.path.join(root_path) + "/doc/SYNTAX.md", "r") as md_file:
- md = md_file.read()
- with open(os.path.join(root_path) + "/doc/SYNTAX.html", "r") as html_file:
- html = html_file.read()
- result = markdown(md)
- self.assertTrue(result == html)
+def test_full_markdown_syntax():
+ root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ with open(os.path.join(root_path) + "/doc/SYNTAX.md", "r") as md_file:
+ md = md_file.read()
+ with open(os.path.join(root_path) + "/doc/SYNTAX.html", "r") as html_file:
+ html = html_file.read()
+ result = markdown(md)
+ assert result == html
class PageHandlingTest(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.root = User.objects.get(username="root")
+ cls.root_group = Group.objects.get(name="Root")
+
def setUp(self):
- self.client.login(username="root", password="plop")
- self.root_group = Group.objects.get(name="Root")
+ self.client.force_login(self.root)
def test_create_page_ok(self):
- """
- Should create a page correctly
- """
+ """Should create a page correctly."""
response = self.client.post(
reverse("core:page_new"),
@@ -301,19 +138,17 @@ class PageHandlingTest(TestCase):
self.assertRedirects(
response, reverse("core:page", kwargs={"page_name": "guy"})
)
- self.assertTrue(Page.objects.filter(name="guy").exists())
+ assert Page.objects.filter(name="guy").exists()
response = self.client.get(reverse("core:page", kwargs={"page_name": "guy"}))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
html = response.content.decode()
- self.assertIn('
', html)
- self.assertIn('', html)
- self.assertIn('', html)
+ assert '' in html
+ assert '' in html
+ assert '' in html
def test_create_child_page_ok(self):
- """
- Should create a page correctly
- """
+ """Should create a page correctly."""
# remove all other pages to make sure there is no side effect
Page.objects.all().delete()
self.client.post(
@@ -321,7 +156,7 @@ class PageHandlingTest(TestCase):
{"parent": "", "name": "guy", "owner_group": str(self.root_group.id)},
)
page = Page.objects.first()
- response = self.client.post(
+ self.client.post(
reverse("core:page_new"),
{
"parent": str(page.id),
@@ -332,8 +167,8 @@ class PageHandlingTest(TestCase):
response = self.client.get(
reverse("core:page", kwargs={"page_name": "guy/bibou"})
)
- self.assertTrue(response.status_code == 200)
- self.assertTrue('' in str(response.content))
+ assert response.status_code == 200
+ assert '' in str(response.content)
def test_access_child_page_ok(self):
"""
@@ -346,7 +181,7 @@ class PageHandlingTest(TestCase):
response = self.client.get(
reverse("core:page", kwargs={"page_name": "guy/bibou"})
)
- self.assertTrue(response.status_code == 200)
+ assert response.status_code == 200
html = response.content.decode()
self.assertIn('', html)
@@ -355,7 +190,7 @@ class PageHandlingTest(TestCase):
Should not display a page correctly
"""
response = self.client.get(reverse("core:page", kwargs={"page_name": "swagg"}))
- self.assertTrue(response.status_code == 200)
+ assert response.status_code == 200
html = response.content.decode()
self.assertIn('', html)
@@ -383,8 +218,8 @@ http://git.an
},
)
response = self.client.get(reverse("core:page", kwargs={"page_name": "guy"}))
- self.assertTrue(response.status_code == 200)
- self.assertTrue(
+ assert response.status_code == 200
+ assert (
'Guy bibou
\\nhttp://git.an
\\n'
+ "Swag
\\n<guy>Bibou</guy>"
+ "<script>alert(\\'Guy\\');</script>"
@@ -392,35 +227,19 @@ http://git.an
)
-class UserToolsTest(TestCase):
- def test_anonymous_user_unauthorized(self):
- response = self.client.get(reverse("core:user_tools"))
- self.assertEqual(response.status_code, 403)
+class UserToolsTest:
+ def test_anonymous_user_unauthorized(self, client):
+ """An anonymous user shouldn't have access to the tools page"""
+ response = client.get(reverse("core:user_tools"))
+ assert response.status_code == 403
- def test_page_is_working(self):
+ @pytest.mark.parametrize("username", ["guy", "root", "skia", "comunity"])
+ def test_page_is_working(self, client, username):
+ """All existing users should be able to see the test page"""
# Test for simple user
- self.client.login(username="guy", password="plop")
- response = self.client.get(reverse("core:user_tools"))
- self.assertNotEqual(response.status_code, 500)
- self.assertEqual(response.status_code, 200)
-
- # Test for root
- self.client.login(username="root", password="plop")
- response = self.client.get(reverse("core:user_tools"))
- self.assertNotEqual(response.status_code, 500)
- self.assertEqual(response.status_code, 200)
-
- # Test for skia
- self.client.login(username="skia", password="plop")
- response = self.client.get(reverse("core:user_tools"))
- self.assertNotEqual(response.status_code, 500)
- self.assertEqual(response.status_code, 200)
-
- # Test for comunity
- self.client.login(username="comunity", password="plop")
- response = self.client.get(reverse("core:user_tools"))
- self.assertNotEqual(response.status_code, 500)
- self.assertEqual(response.status_code, 200)
+ client.force_login(User.objects.get(username=username))
+ response = client.get(reverse("core:user_tools"))
+ assert response.status_code == 200
# TODO: many tests on the pages:
@@ -442,12 +261,12 @@ class FileHandlingTest(TestCase):
reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}),
{"folder_name": "GUY_folder_test"},
)
- self.assertTrue(response.status_code == 302)
+ assert response.status_code == 302
response = self.client.get(
reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id})
)
- self.assertTrue(response.status_code == 200)
- self.assertTrue("GUY_folder_test" in str(response.content))
+ assert response.status_code == 200
+ assert "GUY_folder_test" in str(response.content)
def test_upload_file_home(self):
with open("/bin/ls", "rb") as f:
@@ -457,12 +276,12 @@ class FileHandlingTest(TestCase):
),
{"file_field": f},
)
- self.assertTrue(response.status_code == 302)
+ assert response.status_code == 302
response = self.client.get(
reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id})
)
- self.assertTrue(response.status_code == 200)
- self.assertTrue("ls" in str(response.content))
+ assert response.status_code == 200
+ assert "ls" in str(response.content)
class UserIsInGroupTest(TestCase):
@@ -477,6 +296,10 @@ class UserIsInGroupTest(TestCase):
cls.root_group = Group.objects.get(name="Root")
cls.public = Group.objects.get(name="Public")
+ cls.skia = User.objects.get(username="skia")
+ cls.toto = User.objects.create(
+ username="toto", first_name="a", last_name="b", email="a.b@toto.fr"
+ )
cls.subscribers = Group.objects.get(name="Subscribers")
cls.old_subscribers = Group.objects.get(name="Old subscribers")
cls.accounting_admin = Group.objects.get(name="Accounting admin")
@@ -493,21 +316,15 @@ class UserIsInGroupTest(TestCase):
)
cls.main_club = Club.objects.get(id=1)
- def setUp(self) -> None:
- self.toto = User.objects.create(
- username="toto", first_name="a", last_name="b", email="a.b@toto.fr"
- )
- self.skia = User.objects.get(username="skia")
-
def assert_in_public_group(self, user):
- self.assertTrue(user.is_in_group(pk=self.public.id))
- self.assertTrue(user.is_in_group(name=self.public.name))
+ assert user.is_in_group(pk=self.public.id)
+ assert user.is_in_group(name=self.public.name)
def assert_in_club_metagroups(self, user, club):
meta_groups_board = club.unix_name + settings.SITH_BOARD_SUFFIX
meta_groups_members = club.unix_name + settings.SITH_MEMBER_SUFFIX
- self.assertFalse(user.is_in_group(name=meta_groups_board))
- self.assertFalse(user.is_in_group(name=meta_groups_members))
+ assert user.is_in_group(name=meta_groups_board) is False
+ assert user.is_in_group(name=meta_groups_members) is False
def assert_only_in_public_group(self, user):
self.assert_in_public_group(user)
@@ -519,12 +336,12 @@ class UserIsInGroupTest(TestCase):
self.subscribers,
self.old_subscribers,
):
- self.assertFalse(user.is_in_group(pk=group.pk))
- self.assertFalse(user.is_in_group(name=group.name))
+ assert not user.is_in_group(pk=group.pk)
+ assert not user.is_in_group(name=group.name)
meta_groups_board = self.club.unix_name + settings.SITH_BOARD_SUFFIX
meta_groups_members = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
- self.assertFalse(user.is_in_group(name=meta_groups_board))
- self.assertFalse(user.is_in_group(name=meta_groups_members))
+ assert user.is_in_group(name=meta_groups_board) is False
+ assert user.is_in_group(name=meta_groups_members) is False
def test_anonymous_user(self):
"""
@@ -583,15 +400,13 @@ class UserIsInGroupTest(TestCase):
)
meta_groups_members = self.club.unix_name + settings.SITH_MEMBER_SUFFIX
cache.clear()
- self.assertTrue(self.toto.is_in_group(name=meta_groups_members))
- self.assertEqual(
- membership, cache.get(f"membership_{self.club.id}_{self.toto.id}")
- )
+ assert self.toto.is_in_group(name=meta_groups_members) is True
+ assert membership == cache.get(f"membership_{self.club.id}_{self.toto.id}")
membership.end_date = now() - timedelta(minutes=5)
membership.save()
cached_membership = cache.get(f"membership_{self.club.id}_{self.toto.id}")
- self.assertEqual(cached_membership, "not_member")
- self.assertFalse(self.toto.is_in_group(name=meta_groups_members))
+ assert cached_membership == "not_member"
+ assert self.toto.is_in_group(name=meta_groups_members) is False
def test_cache_properly_cleared_group(self):
"""
@@ -600,24 +415,24 @@ class UserIsInGroupTest(TestCase):
"""
# testing with pk
self.toto.groups.add(self.com_admin.pk)
- self.assertTrue(self.toto.is_in_group(pk=self.com_admin.pk))
+ assert self.toto.is_in_group(pk=self.com_admin.pk) is True
self.toto.groups.remove(self.com_admin.pk)
- self.assertFalse(self.toto.is_in_group(pk=self.com_admin.pk))
+ assert self.toto.is_in_group(pk=self.com_admin.pk) is False
# testing with name
self.toto.groups.add(self.sas_admin.pk)
- self.assertTrue(self.toto.is_in_group(name="SAS admin"))
+ assert self.toto.is_in_group(name="SAS admin") is True
self.toto.groups.remove(self.sas_admin.pk)
- self.assertFalse(self.toto.is_in_group(name="SAS admin"))
+ assert self.toto.is_in_group(name="SAS admin") is False
def test_not_existing_group(self):
"""
Test that searching for a not existing group
returns False
"""
- self.assertFalse(self.skia.is_in_group(name="This doesn't exist"))
+ assert self.skia.is_in_group(name="This doesn't exist") is False
class DateUtilsTest(TestCase):
@@ -639,29 +454,25 @@ class DateUtilsTest(TestCase):
"""
Test that the get_semester function returns the correct semester string
"""
- self.assertEqual(get_semester_code(self.autumn_semester_january), "A24")
- self.assertEqual(get_semester_code(self.autumn_semester_september), "A24")
- self.assertEqual(get_semester_code(self.autumn_first_day), "A24")
+ assert get_semester_code(self.autumn_semester_january) == "A24"
+ assert get_semester_code(self.autumn_semester_september) == "A24"
+ assert get_semester_code(self.autumn_first_day) == "A24"
- self.assertEqual(get_semester_code(self.spring_semester_march), "P23")
- self.assertEqual(get_semester_code(self.spring_first_day), "P23")
+ assert get_semester_code(self.spring_semester_march) == "P23"
+ assert get_semester_code(self.spring_first_day) == "P23"
def test_get_start_of_semester_fixed_date(self):
"""
Test that the get_start_of_semester correctly the starting date of the semester.
"""
automn_2024 = date(2024, self.autumn_month, self.autumn_day)
- self.assertEqual(
- get_start_of_semester(self.autumn_semester_january), automn_2024
- )
- self.assertEqual(
- get_start_of_semester(self.autumn_semester_september), automn_2024
- )
- self.assertEqual(get_start_of_semester(self.autumn_first_day), automn_2024)
+ assert get_start_of_semester(self.autumn_semester_january) == automn_2024
+ assert get_start_of_semester(self.autumn_semester_september) == automn_2024
+ assert get_start_of_semester(self.autumn_first_day) == automn_2024
spring_2023 = date(2023, self.spring_month, self.spring_day)
- self.assertEqual(get_start_of_semester(self.spring_semester_march), spring_2023)
- self.assertEqual(get_start_of_semester(self.spring_first_day), spring_2023)
+ assert get_start_of_semester(self.spring_semester_march) == spring_2023
+ assert get_start_of_semester(self.spring_first_day) == spring_2023
def test_get_start_of_semester_today(self):
"""
@@ -669,10 +480,10 @@ class DateUtilsTest(TestCase):
when no date is given
"""
with freezegun.freeze_time(self.autumn_semester_september):
- self.assertEqual(get_start_of_semester(), self.autumn_first_day)
+ assert get_start_of_semester() == self.autumn_first_day
with freezegun.freeze_time(self.spring_semester_march):
- self.assertEqual(get_start_of_semester(), self.spring_first_day)
+ assert get_start_of_semester() == self.spring_first_day
def test_get_start_of_semester_changing_date(self):
"""
@@ -685,8 +496,8 @@ class DateUtilsTest(TestCase):
mid_autumn = autumn_2023 + timedelta(days=45)
with freezegun.freeze_time(mid_spring) as frozen_time:
- self.assertEqual(get_start_of_semester(), spring_2023)
+ assert get_start_of_semester() == spring_2023
# forward time to the middle of the next semester
frozen_time.move_to(mid_autumn)
- self.assertEqual(get_start_of_semester(), autumn_2023)
+ assert get_start_of_semester() == autumn_2023
diff --git a/core/urls.py b/core/urls.py
index ec42f880..3f6398e8 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -25,12 +25,12 @@
from django.urls import path, re_path, register_converter
-from core.views import *
from core.converters import (
+ BooleanStringConverter,
FourDigitYearConverter,
TwoDigitMonthConverter,
- BooleanStringConverter,
)
+from core.views import *
register_converter(FourDigitYearConverter, "yyyy")
register_converter(TwoDigitMonthConverter, "mm")
diff --git a/core/utils.py b/core/utils.py
index d30e3ebf..427786fe 100644
--- a/core/utils.py
+++ b/core/utils.py
@@ -26,8 +26,9 @@ from typing import Optional
import PIL
from django.conf import settings
from django.core.files.base import ContentFile
-from PIL import ExifTags
from django.utils import timezone
+from PIL import ExifTags
+from PIL.Image import Resampling
def get_git_revision_short_hash() -> str:
@@ -109,7 +110,8 @@ def resize_image(im, edge, format):
(w, h) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge)
content = BytesIO()
- im = im.resize((width, height), PIL.Image.ANTIALIAS)
+ # use the lanczos filter for antialiasing
+ im = im.resize((width, height), Resampling.LANCZOS)
try:
im.save(
fp=content,
diff --git a/core/views/__init__.py b/core/views/__init__.py
index fc807663..ad86fe2d 100644
--- a/core/views/__init__.py
+++ b/core/views/__init__.py
@@ -25,26 +25,21 @@
import types
-from sentry_sdk import last_event_id
-from django.shortcuts import render
+from django.core.exceptions import (
+ ImproperlyConfigured,
+ PermissionDenied,
+)
from django.http import (
HttpResponseForbidden,
HttpResponseNotFound,
HttpResponseServerError,
)
-from django.template import RequestContext
-from django.core.exceptions import (
- PermissionDenied,
- ObjectDoesNotExist,
- ImproperlyConfigured,
-)
-from django.views.generic.base import View
-from django.views.generic.edit import FormView
-from django.views.generic.detail import SingleObjectMixin
from django.utils.functional import cached_property
-from django.db.models import Count
+from django.views.generic.base import View
+from django.views.generic.detail import SingleObjectMixin
+from django.views.generic.edit import FormView
+from sentry_sdk import last_event_id
-from core.models import Group
from core.views.forms import LoginForm
@@ -314,9 +309,8 @@ class QuickNotifMixin:
quick_notif_list = []
def dispatch(self, request, *arg, **kwargs):
- self.quick_notif_list = (
- []
- ) # In some cases, the class can stay instanciated, so we need to reset the list
+ # In some cases, the class can stay instanciated, so we need to reset the list
+ self.quick_notif_list = []
return super(QuickNotifMixin, self).dispatch(request, *arg, **kwargs)
def get_success_url(self):
@@ -362,8 +356,8 @@ class DetailFormView(SingleObjectMixin, FormView):
return super(DetailFormView, self).get_object()
-from .user import *
-from .page import *
from .files import *
-from .site import *
from .group import *
+from .page import *
+from .site import *
+from .user import *
diff --git a/core/views/files.py b/core/views/files.py
index 1047f381..7cce06e7 100644
--- a/core/views/files.py
+++ b/core/views/files.py
@@ -15,29 +15,28 @@
#
# This file contains all the views that concern the page model
-from django.shortcuts import redirect, get_object_or_404
-from django.utils.http import http_date
-from django.views.generic import ListView, DetailView, TemplateView
-from django.views.generic.edit import UpdateView, FormMixin, DeleteView
-from django.views.generic.detail import SingleObjectMixin
-from django.forms.models import modelform_factory
-from django.conf import settings
-from django.utils.translation import gettext_lazy as _
-from django.http import Http404, HttpResponse
-from wsgiref.util import FileWrapper
-from django.urls import reverse
-from django.core.exceptions import PermissionDenied
-from django import forms
-
import os
+from wsgiref.util import FileWrapper
from ajax_select import make_ajax_field
+from django import forms
+from django.conf import settings
+from django.core.exceptions import PermissionDenied
+from django.forms.models import modelform_factory
+from django.http import Http404, HttpResponse
+from django.shortcuts import get_object_or_404, redirect
+from django.urls import reverse
+from django.utils.http import http_date
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, ListView, TemplateView
+from django.views.generic.detail import SingleObjectMixin
+from django.views.generic.edit import DeleteView, FormMixin, UpdateView
-from core.models import SithFile, RealGroup, Notification
+from core.models import Notification, RealGroup, SithFile
from core.views import (
- CanViewMixin,
CanEditMixin,
CanEditPropMixin,
+ CanViewMixin,
can_view,
)
from counter.models import Counter
@@ -79,12 +78,35 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
return response
+class MultipleFileInput(forms.ClearableFileInput):
+ allow_multiple_selected = True
+
+
+class _MultipleFieldMixin:
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault("widget", MultipleFileInput())
+ super().__init__(*args, **kwargs)
+
+ def clean(self, data, initial=None):
+ single_file_clean = super().clean
+ if isinstance(data, (list, tuple)):
+ result = [single_file_clean(d, initial) for d in data]
+ else:
+ result = [single_file_clean(data, initial)]
+ return result
+
+
+class MultipleFileField(_MultipleFieldMixin, forms.FileField): ...
+
+
+class MultipleImageField(_MultipleFieldMixin, forms.ImageField): ...
+
+
class AddFilesForm(forms.Form):
folder_name = forms.CharField(
label=_("Add a new folder"), max_length=30, required=False
)
- file_field = forms.FileField(
- widget=forms.ClearableFileInput(attrs={"multiple": True}),
+ file_field = MultipleFileField(
label=_("Files"),
required=False,
)
diff --git a/core/views/forms.py b/core/views/forms.py
index 936abb26..5426ef14 100644
--- a/core/views/forms.py
+++ b/core/views/forms.py
@@ -21,40 +21,37 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
+import datetime
+import re
+from io import BytesIO
+
+from ajax_select import make_ajax_field
+from ajax_select.fields import AutoCompleteSelectField
from captcha.fields import CaptchaField
-from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django import forms
from django.conf import settings
-from django.db import transaction
-from django.templatetags.static import static
-from django.urls import reverse
+from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.core.exceptions import ValidationError
+from django.db import transaction
from django.forms import (
CheckboxSelectMultiple,
- Select,
DateInput,
- TextInput,
DateTimeInput,
Textarea,
+ TextInput,
)
-from django.utils.translation import gettext_lazy as _
-from django.utils.translation import gettext
-from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
-from ajax_select.fields import AutoCompleteSelectField
-from ajax_select import make_ajax_field
-from django.utils.dateparse import parse_datetime
-from django.utils import timezone
-import datetime
from django.forms.utils import to_current_timezone
-
-import re
-
-from core.models import User, Page, SithFile, Gift
-
-from core.utils import resize_image
-from io import BytesIO
+from django.templatetags.static import static
+from django.urls import reverse
+from django.utils import timezone
+from django.utils.dateparse import parse_datetime
+from django.utils.translation import gettext
+from django.utils.translation import gettext_lazy as _
+from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
from PIL import Image
+from core.models import Gift, Page, SithFile, User
+from core.utils import resize_image
# Widgets
diff --git a/core/views/group.py b/core/views/group.py
index a6b61866..1c598832 100644
--- a/core/views/group.py
+++ b/core/views/group.py
@@ -15,18 +15,15 @@
#
"""
- This module contains views to manage Groups
+This module contains views to manage Groups
"""
-from django.views.generic.edit import UpdateView, CreateView, DeleteView
-from django.views.generic import ListView
-from django.views.generic.edit import FormView
-from django.urls import reverse_lazy
-from django.shortcuts import get_object_or_404
-from django.utils.translation import gettext_lazy as _
-from django import forms
-
from ajax_select.fields import AutoCompleteSelectMultipleField
+from django import forms
+from django.urls import reverse_lazy
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import ListView
+from django.views.generic.edit import CreateView, DeleteView, UpdateView
from core.models import RealGroup, User
from core.views import CanCreateMixin, CanEditMixin, DetailFormView
diff --git a/core/views/page.py b/core/views/page.py
index c2c7dbce..5f148235 100644
--- a/core/views/page.py
+++ b/core/views/page.py
@@ -15,16 +15,16 @@
#
# This file contains all the views that concern the page model
-from django.urls import reverse_lazy
-from django.views.generic import ListView, DetailView
-from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.forms.models import modelform_factory
from django.http import Http404
from django.shortcuts import redirect
+from django.urls import reverse_lazy
+from django.views.generic import DetailView, ListView
+from django.views.generic.edit import CreateView, DeleteView, UpdateView
-from core.models import Page, PageRev, LockError
+from core.models import LockError, Page, PageRev
+from core.views import CanCreateMixin, CanEditMixin, CanEditPropMixin, CanViewMixin
from core.views.forms import MarkdownInput, PageForm, PagePropForm
-from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
class CanEditPagePropMixin(CanEditPropMixin):
diff --git a/core/views/site.py b/core/views/site.py
index c34cf2c4..bdd575f4 100644
--- a/core/views/site.py
+++ b/core/views/site.py
@@ -23,23 +23,22 @@
#
#
-from django.shortcuts import render, redirect
-from django.http import JsonResponse
-from django.core import serializers
-from django.contrib.auth.decorators import login_required
-from django.utils import html
-from django.views.generic import ListView, TemplateView
-from django.conf import settings
-from django.utils.text import slugify
-from django.db.models.query import QuerySet
-
import json
+from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.core import serializers
+from django.db.models.query import QuerySet
+from django.http import JsonResponse
+from django.shortcuts import redirect, render
+from django.utils import html
+from django.utils.text import slugify
+from django.views.generic import ListView, TemplateView
from haystack.query import SearchQuerySet
-from core.models import User, Notification
-from core.utils import doku_to_markdown, bbcode_to_markdown
from club.models import Club
+from core.models import Notification, User
+from core.utils import bbcode_to_markdown, doku_to_markdown
def index(request, context=None):
@@ -100,9 +99,8 @@ def search_club(query, as_json=False):
if query:
clubs = Club.objects.filter(name__icontains=query).all()
clubs = clubs[:5]
- if (
- as_json
- ): # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
+ if as_json:
+ # Re-loads json to avoid double encoding by JsonResponse, but still benefit from serializers
clubs = json.loads(serializers.serialize("json", clubs, fields=("name")))
else:
clubs = list(clubs)
diff --git a/core/views/user.py b/core/views/user.py
index dbd60f13..d8d8d909 100644
--- a/core/views/user.py
+++ b/core/views/user.py
@@ -24,50 +24,49 @@
#
# This file contains all the views that concern the user model
-from django.shortcuts import render, redirect, get_object_or_404
+import logging
+from datetime import date, timedelta
+
+from django.conf import settings
from django.contrib.auth import views
from django.contrib.auth.forms import PasswordChangeForm
-from django.utils.translation import gettext as _
-from django.urls import reverse
from django.core.exceptions import PermissionDenied, ValidationError
+from django.forms import CheckboxSelectMultiple
+from django.forms.models import modelform_factory
from django.http import Http404, HttpResponse
-from django.views.generic.edit import UpdateView
+from django.shortcuts import get_object_or_404, redirect, render
+from django.template.response import TemplateResponse
+from django.urls import reverse, reverse_lazy
+from django.utils.translation import gettext as _
from django.views.generic import (
- ListView,
- DetailView,
- TemplateView,
CreateView,
DeleteView,
+ DetailView,
+ ListView,
+ TemplateView,
)
-from django.forms.models import modelform_factory
-from django.forms import CheckboxSelectMultiple
-from django.urls import reverse_lazy
-from django.template.response import TemplateResponse
-from django.conf import settings
-from django.views.generic.dates import YearMixin, MonthMixin
+from django.views.generic.dates import MonthMixin, YearMixin
+from django.views.generic.edit import UpdateView
-from datetime import timedelta, date
-import logging
from api.views.sas import all_pictures_of_user
-
+from core.models import Gift, Preferences, SithFile, User
from core.views import (
- CanViewMixin,
CanEditMixin,
CanEditPropMixin,
- UserIsLoggedMixin,
- TabedViewMixin,
+ CanViewMixin,
QuickNotifMixin,
+ TabedViewMixin,
+ UserIsLoggedMixin,
)
from core.views.forms import (
- RegisteringForm,
- UserProfileForm,
- LoginForm,
- UserGodfathersForm,
GiftForm,
+ LoginForm,
+ RegisteringForm,
+ UserGodfathersForm,
+ UserProfileForm,
)
-from core.models import User, SithFile, Preferences, Gift
-from subscription.models import Subscription
from counter.forms import StudentCardForm
+from subscription.models import Subscription
from trombi.views import UserTrombiForm
@@ -501,9 +500,10 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
def get_context_data(self, **kwargs):
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
- from counter.models import Counter
from django.db.models import Sum
+ from counter.models import Counter
+
foyer = Counter.objects.filter(name="Foyer").first()
mde = Counter.objects.filter(name="MDE").first()
gommette = Counter.objects.filter(name="La Gommette").first()
@@ -601,10 +601,12 @@ class UserUploadProfilePictView(CanEditMixin, DetailView):
template_name = "core/user_edit.jinja"
def post(self, request, *args, **kwargs):
- from core.utils import resize_image
from io import BytesIO
+
from PIL import Image
+ from core.utils import resize_image
+
self.object = self.get_object()
if self.object.profile_pict:
raise ValidationError(_("User already has a profile picture"))
diff --git a/counter/app.py b/counter/app.py
index c1feefe2..a1165d7c 100644
--- a/counter/app.py
+++ b/counter/app.py
@@ -31,4 +31,4 @@ class CounterConfig(AppConfig):
verbose_name = _("counter")
def ready(self):
- import counter.signals
+ import counter.signals # noqa F401
diff --git a/counter/forms.py b/counter/forms.py
index a09236e7..7c282f57 100644
--- a/counter/forms.py
+++ b/counter/forms.py
@@ -3,15 +3,15 @@ from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultip
from django import forms
from django.utils.translation import gettext_lazy as _
-from core.views.forms import TzAwareDateTimeField, SelectDate
+from core.views.forms import SelectDate, TzAwareDateTimeField
from counter.models import (
BillingInfo,
- StudentCard,
- Customer,
- Refilling,
Counter,
- Product,
+ Customer,
Eticket,
+ Product,
+ Refilling,
+ StudentCard,
)
diff --git a/counter/migrations/0001_initial.py b/counter/migrations/0001_initial.py
index e9635e5d..aa84e936 100644
--- a/counter/migrations/0001_initial.py
+++ b/counter/migrations/0001_initial.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-import accounting.models
import django.db.models.deletion
from django.conf import settings
-import django.db.models.deletion
+from django.db import migrations, models
+
+import accounting.models
class Migration(migrations.Migration):
diff --git a/counter/migrations/0002_auto_20160826_1342.py b/counter/migrations/0002_auto_20160826_1342.py
index ccc4288c..83e6a83a 100644
--- a/counter/migrations/0002_auto_20160826_1342.py
+++ b/counter/migrations/0002_auto_20160826_1342.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
-import accounting.models
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+import accounting.models
class Migration(migrations.Migration):
diff --git a/counter/migrations/0003_permanency_activity.py b/counter/migrations/0003_permanency_activity.py
index be4e9962..915be8fc 100644
--- a/counter/migrations/0003_permanency_activity.py
+++ b/counter/migrations/0003_permanency_activity.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import datetime
-from django.utils.timezone import utc
+from datetime import timezone
+
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -16,7 +17,9 @@ class Migration(migrations.Migration):
field=models.DateTimeField(
verbose_name="activity time",
auto_now=True,
- default=datetime.datetime(2016, 8, 26, 17, 5, 31, 202824, tzinfo=utc),
+ default=datetime.datetime(
+ 2016, 8, 26, 17, 5, 31, 202824, tzinfo=timezone.utc
+ ),
),
preserve_default=False,
)
diff --git a/counter/migrations/0005_auto_20160826_2330.py b/counter/migrations/0005_auto_20160826_2330.py
index 8e0df744..9dc3fad9 100644
--- a/counter/migrations/0005_auto_20160826_2330.py
+++ b/counter/migrations/0005_auto_20160826_2330.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/counter/migrations/0009_eticket.py b/counter/migrations/0009_eticket.py
index 68b675ef..96d82e30 100644
--- a/counter/migrations/0009_eticket.py
+++ b/counter/migrations/0009_eticket.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/counter/migrations/0013_customer_recorded_products.py b/counter/migrations/0013_customer_recorded_products.py
index d6b3cfa8..271491eb 100644
--- a/counter/migrations/0013_customer_recorded_products.py
+++ b/counter/migrations/0013_customer_recorded_products.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.utils.translation import gettext_lazy as _
-from django.db import migrations, models
from django.conf import settings
+from django.db import migrations, models
+from django.utils.translation import gettext_lazy as _
from core.models import User
-from counter.models import Customer, Product, Selling, Counter
+from counter.models import Counter, Customer, Product, Selling
def balance_ecocups(apps, schema_editor):
diff --git a/counter/migrations/0015_merge.py b/counter/migrations/0015_merge.py
index 6dcff021..eba94807 100644
--- a/counter/migrations/0015_merge.py
+++ b/counter/migrations/0015_merge.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
+from django.db import migrations
class Migration(migrations.Migration):
diff --git a/counter/migrations/0017_studentcard.py b/counter/migrations/0017_studentcard.py
index a2f62222..267cef25 100644
--- a/counter/migrations/0017_studentcard.py
+++ b/counter/migrations/0017_studentcard.py
@@ -3,8 +3,8 @@
from __future__ import unicode_literals
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/counter/migrations/0019_billinginfo.py b/counter/migrations/0019_billinginfo.py
index c4c74c39..b348c86a 100644
--- a/counter/migrations/0019_billinginfo.py
+++ b/counter/migrations/0019_billinginfo.py
@@ -1,8 +1,8 @@
# Generated by Django 3.2.16 on 2023-01-08 12:49
-from django.db import migrations, models
import django.db.models.deletion
import django_countries.fields
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/counter/migrations/0020_auto_20221215_1709.py b/counter/migrations/0020_auto_20221215_1709.py
index 52010133..22db406b 100644
--- a/counter/migrations/0020_auto_20221215_1709.py
+++ b/counter/migrations/0020_auto_20221215_1709.py
@@ -1,8 +1,9 @@
# Generated by Django 3.2.16 on 2022-12-15 16:09
-import accounting.models
from django.db import migrations
+import accounting.models
+
class Migration(migrations.Migration):
dependencies = [
diff --git a/counter/migrations/0021_rename_check_cashregistersummaryitem_is_checked.py b/counter/migrations/0021_rename_check_cashregistersummaryitem_is_checked.py
new file mode 100644
index 00000000..3ccc2744
--- /dev/null
+++ b/counter/migrations/0021_rename_check_cashregistersummaryitem_is_checked.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.2 on 2024-06-26 09:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [("counter", "0020_auto_20221215_1709")]
+
+ operations = [
+ migrations.RenameField(
+ model_name="cashregistersummaryitem",
+ old_name="check",
+ new_name="is_check",
+ ),
+ migrations.AlterField(
+ model_name="cashregistersummaryitem",
+ name="is_check",
+ field=models.BooleanField(
+ default=False,
+ help_text="True if this is a bank check, else False",
+ verbose_name="check",
+ ),
+ ),
+ ]
diff --git a/counter/models.py b/counter/models.py
index 476aaf13..44818da5 100644
--- a/counter/models.py
+++ b/counter/models.py
@@ -15,35 +15,34 @@
#
from __future__ import annotations
-from typing import Tuple, Optional
-
-from django.db import models
-from django.db.models import F, Value, Sum, QuerySet, OuterRef, Exists
-from django.db.models.functions import Concat, Length
-from django.utils.translation import gettext_lazy as _
-from django.utils import timezone
-from django.conf import settings
-from django.urls import reverse
-from django.core.validators import MinLengthValidator
-from django.forms import ValidationError
-from django.utils.functional import cached_property
-
-from datetime import timedelta, date, datetime
+import base64
+import os
import random
import string
-import os
-import base64
-from dict2xml import dict2xml
+from datetime import date, datetime, timedelta
+from datetime import timezone as tz
+from typing import Tuple
+from dict2xml import dict2xml
+from django.conf import settings
+from django.core.validators import MinLengthValidator
+from django.db import models
+from django.db.models import Exists, F, OuterRef, QuerySet, Sum, Value
+from django.db.models.functions import Concat, Length
+from django.forms import ValidationError
+from django.urls import reverse
+from django.utils import timezone
+from django.utils.functional import cached_property
+from django.utils.translation import gettext_lazy as _
+from django_countries.fields import CountryField
+
+from accounting.models import CurrencyField
+from club.models import Club
+from core.models import Group, Notification, User
from core.utils import get_start_of_semester
from sith.settings import SITH_COUNTER_OFFICES, SITH_MAIN_CLUB
-from club.models import Club, Membership
-from accounting.models import CurrencyField
-from core.models import Group, User, Notification
from subscription.models import Subscription
-from django_countries.fields import CountryField
-
class Customer(models.Model):
"""
@@ -536,7 +535,7 @@ class Counter(models.Model):
.order_by("-perm_sum")
)
- def get_top_customers(self, since: Optional[date] = None) -> QuerySet:
+ def get_top_customers(self, since: datetime | date | None = None) -> QuerySet:
"""
Return a QuerySet querying the money spent by customers of this counter
since the specified date, ordered by descending amount of money spent.
@@ -545,9 +544,13 @@ class Counter(models.Model):
- the full name (first name + last name) of the customer
- the nickname of the customer
- the amount of money spent by the customer
+
+ :param since: timestamp from which to perform the calculation
"""
if since is None:
since = get_start_of_semester()
+ if isinstance(since, date):
+ since = datetime(since.year, since.month, since.day, tzinfo=tz.utc)
return (
self.sellings.filter(date__gte=since)
.annotate(
@@ -570,19 +573,18 @@ class Counter(models.Model):
.order_by("-selling_sum")
)
- def get_total_sales(self, since=None) -> CurrencyField:
+ def get_total_sales(self, since: datetime | date | None = None) -> CurrencyField:
"""
Compute and return the total turnover of this counter
since the date specified in parameter (by default, since the start of the current
semester)
:param since: timestamp from which to perform the calculation
- :type since: datetime | date | None
:return: Total revenue earned at this counter
"""
if since is None:
since = get_start_of_semester()
if isinstance(since, date):
- since = datetime.combine(since, datetime.min.time())
+ since = datetime(since.year, since.month, since.day, tzinfo=tz.utc)
total = self.sellings.filter(date__gte=since).aggregate(
total=Sum(F("quantity") * F("unit_price"), output_field=CurrencyField())
)["total"]
@@ -929,25 +931,25 @@ class CashRegisterSummary(models.Model):
if name[:5] == "check":
checks = self.items.filter(check=True).order_by("value").all()
if name == "ten_cents":
- return self.items.filter(value=0.1, check=False).first()
+ return self.items.filter(value=0.1, is_check=False).first()
elif name == "twenty_cents":
- return self.items.filter(value=0.2, check=False).first()
+ return self.items.filter(value=0.2, is_check=False).first()
elif name == "fifty_cents":
- return self.items.filter(value=0.5, check=False).first()
+ return self.items.filter(value=0.5, is_check=False).first()
elif name == "one_euro":
- return self.items.filter(value=1, check=False).first()
+ return self.items.filter(value=1, is_check=False).first()
elif name == "two_euros":
- return self.items.filter(value=2, check=False).first()
+ return self.items.filter(value=2, is_check=False).first()
elif name == "five_euros":
- return self.items.filter(value=5, check=False).first()
+ return self.items.filter(value=5, is_check=False).first()
elif name == "ten_euros":
- return self.items.filter(value=10, check=False).first()
+ return self.items.filter(value=10, is_check=False).first()
elif name == "twenty_euros":
- return self.items.filter(value=20, check=False).first()
+ return self.items.filter(value=20, is_check=False).first()
elif name == "fifty_euros":
- return self.items.filter(value=50, check=False).first()
+ return self.items.filter(value=50, is_check=False).first()
elif name == "hundred_euros":
- return self.items.filter(value=100, check=False).first()
+ return self.items.filter(value=100, is_check=False).first()
elif name == "check_1":
return checks[0] if 0 < len(checks) else None
elif name == "check_2":
@@ -995,7 +997,11 @@ class CashRegisterSummaryItem(models.Model):
)
value = CurrencyField(_("value"))
quantity = models.IntegerField(_("quantity"), default=0)
- check = models.BooleanField(_("check"), default=False)
+ is_check = models.BooleanField(
+ _("check"),
+ default=False,
+ help_text=_("True if this is a bank check, else False"),
+ )
class Meta:
verbose_name = _("cash register summary item")
diff --git a/counter/signals.py b/counter/signals.py
index 9c1f4b78..9221494d 100644
--- a/counter/signals.py
+++ b/counter/signals.py
@@ -24,12 +24,10 @@
from django.db.models.signals import pre_delete
from django.dispatch import receiver
-from django.conf import settings
from core.middleware import get_signal_request
from core.models import OperationLog
-
-from counter.models import Selling, Refilling, Counter
+from counter.models import Counter, Refilling, Selling
def write_log(instance, operation_type):
diff --git a/counter/tests.py b/counter/tests.py
index 6079099a..ddfde22c 100644
--- a/counter/tests.py
+++ b/counter/tests.py
@@ -13,20 +13,18 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
-from datetime import date, timedelta
import json
import re
import string
from django.test import TestCase
from django.urls import reverse
-from django.core.management import call_command
from django.utils import timezone
from django.utils.timezone import timedelta
from club.models import Club
from core.models import User
-from counter.models import Counter, Customer, BillingInfo, Permanency, Selling, Product
+from counter.models import BillingInfo, Counter, Customer, Permanency, Product, Selling
from sith.settings import SITH_MAIN_CLUB
@@ -48,9 +46,7 @@ class CounterTest(TestCase):
reverse("counter:details", kwargs={"counter_id": self.mde.id})
)
- self.assertTrue(
- 'class="link-button">S' Kia' in str(response.content)
- )
+ assert 'class="link-button">S' Kia' in str(response.content)
counter_token = re.search(
r'name="counter_token" value="([^"]*)"', str(response.content)
@@ -62,7 +58,7 @@ class CounterTest(TestCase):
)
counter_url = response.get("location")
response = self.client.get(response.get("location"))
- self.assertTrue(">Richard Batsbak" in str(response.content))
+ assert ">Richard Batsbak" in str(response.content)
self.client.post(
counter_url,
@@ -92,11 +88,10 @@ class CounterTest(TestCase):
response_get = self.client.get(response.get("location"))
response_content = response_get.content.decode("utf-8")
- self.assertTrue("2 x Barbar" in str(response_content))
- self.assertTrue("2 x Déconsigne Eco-cup" in str(response_content))
- self.assertTrue(
- "
Client : Richard Batsbak - Nouveau montant : 3.60"
- in str(response_content)
+ assert "2 x Barbar" in str(response_content)
+ assert "2 x Déconsigne Eco-cup" in str(response_content)
+ assert "
Client : Richard Batsbak - Nouveau montant : 3.60" in str(
+ response_content
)
self.client.post(
@@ -113,7 +108,7 @@ class CounterTest(TestCase):
"bank": "OTHER",
},
)
- self.assertTrue(response.status_code == 200)
+ assert response.status_code == 200
self.client.post(
reverse("counter:login", kwargs={"counter_id": self.foyer.id}),
@@ -143,27 +138,26 @@ class CounterTest(TestCase):
"bank": "OTHER",
},
)
- self.assertTrue(response.status_code == 200)
+ assert response.status_code == 200
def test_annotate_has_barman_queryset(self):
"""
Test if the custom queryset method ``annotate_has_barman``
works as intended
"""
- self.sli.counters.clear()
- self.sli.counters.add(self.foyer, self.mde)
+ self.sli.counters.set([self.foyer, self.mde])
counters = Counter.objects.annotate_has_barman(self.sli)
for counter in counters:
if counter.name in ("Foyer", "MDE"):
- self.assertTrue(counter.has_annotated_barman)
+ assert counter.has_annotated_barman
else:
- self.assertFalse(counter.has_annotated_barman)
+ assert not counter.has_annotated_barman
class CounterStatsTest(TestCase):
@classmethod
def setUpTestData(cls):
- cls.counter = Counter.objects.filter(id=2).first()
+ cls.counter = Counter.objects.get(id=2)
cls.krophil = User.objects.get(username="krophil")
cls.skia = User.objects.get(username="skia")
cls.sli = User.objects.get(username="sli")
@@ -264,110 +258,57 @@ class CounterStatsTest(TestCase):
def test_not_authenticated_user_fail(self):
# Test with not login user
response = self.client.get(reverse("counter:stats", args=[self.counter.id]))
- self.assertTrue(response.status_code == 403)
+ assert response.status_code == 403
def test_unauthorized_user_fails(self):
- user = User.objects.get(username="public")
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(User.objects.get(username="public"))
response = self.client.get(reverse("counter:stats", args=[self.counter.id]))
- self.assertTrue(response.status_code == 403)
+ assert response.status_code == 403
def test_get_total_sales(self):
"""
Test the result of the Counter.get_total_sales() method
"""
- total = self.counter.get_total_sales()
- self.assertEqual(total, 3102)
+ assert self.counter.get_total_sales() == 3102
def test_top_barmen(self):
"""
Test the result of Counter.get_top_barmen() is correct
"""
- top = iter(self.counter.get_top_barmen())
- self.assertEqual(
- next(top),
+ users = [self.skia, self.root, self.sli]
+ perm_times = [
+ timedelta(days=16, hours=2, minutes=35, seconds=54),
+ timedelta(hours=21),
+ timedelta(hours=5),
+ ]
+ assert list(self.counter.get_top_barmen()) == [
{
- "user": self.skia.id,
- "name": f"{self.skia.first_name} {self.skia.last_name}",
- "promo": self.skia.promo,
- "nickname": self.skia.nick_name,
- "perm_sum": timedelta(days=16, hours=2, minutes=35, seconds=54),
- },
- )
- self.assertEqual(
- next(top),
- {
- "user": self.root.id,
- "name": f"{self.root.first_name} {self.root.last_name}",
- "promo": self.root.promo,
- "nickname": self.root.nick_name,
- "perm_sum": timedelta(hours=21),
- },
- )
- self.assertEqual(
- next(top),
- {
- "user": self.sli.id,
- "name": f"{self.sli.first_name} {self.sli.last_name}",
- "promo": self.sli.promo,
- "nickname": self.sli.nick_name,
- "perm_sum": timedelta(hours=5),
- },
- )
- self.assertIsNone(
- next(top, None), msg="barmen with no office hours should not be in the top"
- )
+ "user": user.id,
+ "name": f"{user.first_name} {user.last_name}",
+ "promo": user.promo,
+ "nickname": user.nick_name,
+ "perm_sum": perm_time,
+ }
+ for user, perm_time in zip(users, perm_times)
+ ]
def test_top_customer(self):
"""
Test the result of Counter.get_top_customers() is correct
"""
- top = iter(self.counter.get_top_customers())
- expected_results = [
+ users = [self.sli, self.skia, self.krophil, self.root]
+ sale_amounts = [2000, 1000, 100, 2]
+ assert list(self.counter.get_top_customers()) == [
{
- "user": self.sli.id,
- "name": f"{self.sli.first_name} {self.sli.last_name}",
- "promo": self.sli.promo,
- "nickname": self.sli.nick_name,
- "selling_sum": 2000,
- },
- {
- "user": self.skia.id,
- "name": f"{self.skia.first_name} {self.skia.last_name}",
- "promo": self.skia.promo,
- "nickname": self.skia.nick_name,
- "selling_sum": 1000,
- },
- {
- "user": self.krophil.id,
- "name": f"{self.krophil.first_name} {self.krophil.last_name}",
- "promo": self.krophil.promo,
- "nickname": self.krophil.nick_name,
- "selling_sum": 100,
- },
- {
- "user": self.root.id,
- "name": f"{self.root.first_name} {self.root.last_name}",
- "promo": self.root.promo,
- "nickname": self.root.nick_name,
- "selling_sum": 2,
- },
+ "user": user.id,
+ "name": f"{user.first_name} {user.last_name}",
+ "promo": user.promo,
+ "nickname": user.nick_name,
+ "selling_sum": sale_amount,
+ }
+ for user, sale_amount in zip(users, sale_amounts)
]
- for result in expected_results:
- self.assertEqual(
- next(top),
- {
- "user": result["user"],
- "name": result["name"],
- "promo": result["promo"],
- "nickname": result["nickname"],
- "selling_sum": result["selling_sum"],
- },
- )
-
- self.assertIsNone(next(top, None))
-
class BillingInfoTest(TestCase):
@classmethod
@@ -388,13 +329,15 @@ class BillingInfoTest(TestCase):
"city": "Sète",
"country": "FR",
}
+ cls.root = User.objects.get(username="root")
+ cls.subscriber = User.objects.get(username="subscriber")
def test_edit_infos(self):
- user = User.objects.get(username="subscriber")
+ user = self.subscriber
BillingInfo.objects.get_or_create(
customer=user.customer, defaults=self.payload_1
)
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(user)
response = self.client.post(
reverse("counter:edit_billing_info", args=[user.id]),
json.dumps(self.payload_2),
@@ -402,23 +345,23 @@ class BillingInfoTest(TestCase):
)
user = User.objects.get(username="subscriber")
infos = BillingInfo.objects.get(customer__user=user)
- self.assertEqual(200, response.status_code)
+ assert response.status_code == 200
self.assertJSONEqual(response.content, {"errors": None})
- self.assertTrue(hasattr(user.customer, "billing_infos"))
- self.assertEqual(user.customer, infos.customer)
- self.assertEqual("Subscribed", infos.first_name)
- self.assertEqual("User", infos.last_name)
- self.assertEqual("3, rue de Troyes", infos.address_1)
- self.assertEqual(None, infos.address_2)
- self.assertEqual("34301", infos.zip_code)
- self.assertEqual("Sète", infos.city)
- self.assertEqual("FR", infos.country)
+ assert hasattr(user.customer, "billing_infos")
+ assert infos.customer == user.customer
+ assert infos.first_name == "Subscribed"
+ assert infos.last_name == "User"
+ assert infos.address_1 == "3, rue de Troyes"
+ assert infos.address_2 is None
+ assert infos.zip_code == "34301"
+ assert infos.city == "Sète"
+ assert infos.country == "FR"
def test_create_infos_for_user_with_account(self):
user = User.objects.get(username="subscriber")
if hasattr(user.customer, "billing_infos"):
user.customer.billing_infos.delete()
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(user)
response = self.client.post(
reverse("counter:create_billing_info", args=[user.id]),
json.dumps(self.payload_1),
@@ -426,48 +369,48 @@ class BillingInfoTest(TestCase):
)
user = User.objects.get(username="subscriber")
infos = BillingInfo.objects.get(customer__user=user)
- self.assertEqual(200, response.status_code)
+ assert response.status_code == 200
self.assertJSONEqual(response.content, {"errors": None})
- self.assertTrue(hasattr(user.customer, "billing_infos"))
- self.assertEqual(user.customer, infos.customer)
- self.assertEqual("Subscribed", infos.first_name)
- self.assertEqual("User", infos.last_name)
- self.assertEqual("1 rue des Huns", infos.address_1)
- self.assertEqual(None, infos.address_2)
- self.assertEqual("90000", infos.zip_code)
- self.assertEqual("Belfort", infos.city)
- self.assertEqual("FR", infos.country)
+ assert hasattr(user.customer, "billing_infos")
+ assert infos.customer == user.customer
+ assert infos.first_name == "Subscribed"
+ assert infos.last_name == "User"
+ assert infos.address_1 == "1 rue des Huns"
+ assert infos.address_2 is None
+ assert infos.zip_code == "90000"
+ assert infos.city == "Belfort"
+ assert infos.country == "FR"
def test_create_infos_for_user_without_account(self):
user = User.objects.get(username="subscriber")
if hasattr(user, "customer"):
user.customer.delete()
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(user)
response = self.client.post(
reverse("counter:create_billing_info", args=[user.id]),
json.dumps(self.payload_1),
content_type="application/json",
)
user = User.objects.get(username="subscriber")
- self.assertTrue(hasattr(user, "customer"))
- self.assertTrue(hasattr(user.customer, "billing_infos"))
- self.assertEqual(200, response.status_code)
+ assert hasattr(user, "customer")
+ assert hasattr(user.customer, "billing_infos")
+ assert response.status_code == 200
self.assertJSONEqual(response.content, {"errors": None})
infos = BillingInfo.objects.get(customer__user=user)
self.assertEqual(user.customer, infos.customer)
- self.assertEqual("Subscribed", infos.first_name)
- self.assertEqual("User", infos.last_name)
- self.assertEqual("1 rue des Huns", infos.address_1)
- self.assertEqual(None, infos.address_2)
- self.assertEqual("90000", infos.zip_code)
- self.assertEqual("Belfort", infos.city)
- self.assertEqual("FR", infos.country)
+ assert infos.first_name == "Subscribed"
+ assert infos.last_name == "User"
+ assert infos.address_1 == "1 rue des Huns"
+ assert infos.address_2 is None
+ assert infos.zip_code == "90000"
+ assert infos.city == "Belfort"
+ assert infos.country == "FR"
def test_create_invalid(self):
user = User.objects.get(username="subscriber")
if hasattr(user.customer, "billing_infos"):
user.customer.billing_infos.delete()
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(user)
# address_1, zip_code and country are missing
payload = {
"first_name": user.first_name,
@@ -481,7 +424,7 @@ class BillingInfoTest(TestCase):
)
user = User.objects.get(username="subscriber")
self.assertEqual(400, response.status_code)
- self.assertFalse(hasattr(user.customer, "billing_infos"))
+ assert not hasattr(user.customer, "billing_infos")
expected_errors = {
"errors": [
{"field": "Adresse 1", "messages": ["Ce champ est obligatoire."]},
@@ -496,7 +439,7 @@ class BillingInfoTest(TestCase):
BillingInfo.objects.get_or_create(
customer=user.customer, defaults=self.payload_1
)
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(user)
# address_1, zip_code and country are missing
payload = {
"first_name": user.first_name,
@@ -510,7 +453,7 @@ class BillingInfoTest(TestCase):
)
user = User.objects.get(username="subscriber")
self.assertEqual(400, response.status_code)
- self.assertTrue(hasattr(user.customer, "billing_infos"))
+ assert hasattr(user.customer, "billing_infos")
expected_errors = {
"errors": [
{"field": "Adresse 1", "messages": ["Ce champ est obligatoire."]},
@@ -537,7 +480,7 @@ class BillingInfoTest(TestCase):
user = User.objects.get(username="subscriber")
if hasattr(user.customer, "billing_infos"):
user.customer.billing_infos.delete()
- self.client.login(username=user.username, password="plop")
+ self.client.force_login(user)
response = self.client.post(
reverse("counter:edit_billing_info", args=[user.id]),
json.dumps(self.payload_2),
@@ -550,17 +493,17 @@ class BillingInfoTest(TestCase):
BillingInfo.objects.get_or_create(
customer=user.customer, defaults=self.payload_1
)
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
response = self.client.post(
reverse("counter:edit_billing_info", args=[user.id]),
json.dumps(self.payload_2),
content_type="application/json",
)
- self.assertEqual(200, response.status_code)
+ assert response.status_code == 200
user = User.objects.get(username="subscriber")
infos = BillingInfo.objects.get(customer__user=user)
self.assertJSONEqual(response.content, {"errors": None})
- self.assertTrue(hasattr(user.customer, "billing_infos"))
+ assert hasattr(user.customer, "billing_infos")
self.assertEqual(user.customer, infos.customer)
self.assertEqual("Subscribed", infos.first_name)
self.assertEqual("User", infos.last_name)
@@ -574,61 +517,55 @@ class BillingInfoTest(TestCase):
user = User.objects.get(username="subscriber")
if hasattr(user.customer, "billing_infos"):
user.customer.billing_infos.delete()
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
response = self.client.post(
reverse("counter:create_billing_info", args=[user.id]),
json.dumps(self.payload_2),
content_type="application/json",
)
- self.assertEqual(200, response.status_code)
+ assert response.status_code == 200
user = User.objects.get(username="subscriber")
infos = BillingInfo.objects.get(customer__user=user)
self.assertJSONEqual(response.content, {"errors": None})
- self.assertTrue(hasattr(user.customer, "billing_infos"))
- self.assertEqual(user.customer, infos.customer)
- self.assertEqual("Subscribed", infos.first_name)
- self.assertEqual("User", infos.last_name)
- self.assertEqual("3, rue de Troyes", infos.address_1)
- self.assertEqual(None, infos.address_2)
- self.assertEqual("34301", infos.zip_code)
- self.assertEqual("Sète", infos.city)
- self.assertEqual("FR", infos.country)
+ assert hasattr(user.customer, "billing_infos")
+ assert infos.customer == user.customer
+ assert infos.first_name == "Subscribed"
+ assert infos.last_name == "User"
+ assert infos.address_1 == "3, rue de Troyes"
+ assert infos.address_2 is None
+ assert infos.zip_code == "34301"
+ assert infos.city == "Sète"
+ assert infos.country == "FR"
class BarmanConnectionTest(TestCase):
- def setUp(self):
- self.krophil = User.objects.get(username="krophil")
- self.skia = User.objects.get(username="skia")
- self.skia.customer.account = 800
- self.krophil.customer.save()
- self.skia.customer.save()
+ @classmethod
+ def setUpTestData(cls):
+ cls.krophil = User.objects.get(username="krophil")
+ cls.skia = User.objects.get(username="skia")
+ cls.skia.customer.account = 800
+ cls.krophil.customer.save()
+ cls.skia.customer.save()
- self.counter = Counter.objects.filter(id=2).first()
+ cls.counter = Counter.objects.get(id=2)
def test_barman_granted(self):
self.client.post(
reverse("counter:login", args=[self.counter.id]),
{"username": "krophil", "password": "plop"},
)
- response_get = self.client.get(
- reverse("counter:details", args=[self.counter.id])
- )
+ response = self.client.get(reverse("counter:details", args=[self.counter.id]))
- self.assertTrue("
Entrez un code client :
" in str(response_get.content))
+ assert "
Entrez un code client :
" in str(response.content)
def test_counters_list_barmen(self):
self.client.post(
reverse("counter:login", args=[self.counter.id]),
{"username": "krophil", "password": "plop"},
)
- response_get = self.client.get(
- reverse("counter:activity", args=[self.counter.id])
- )
+ response = self.client.get(reverse("counter:activity", args=[self.counter.id]))
- self.assertTrue(
- '
Kro Phil''
- in str(response_get.content)
- )
+ assert '
Kro Phil'' in str(response.content)
def test_barman_denied(self):
self.client.post(
@@ -639,20 +576,16 @@ class BarmanConnectionTest(TestCase):
reverse("counter:details", args=[self.counter.id])
)
- self.assertTrue("
Merci de vous identifier
" in str(response_get.content))
+ assert "
Merci de vous identifier
" in str(response_get.content)
def test_counters_list_no_barmen(self):
self.client.post(
reverse("counter:login", args=[self.counter.id]),
{"username": "krophil", "password": "plop"},
)
- response_get = self.client.get(
- reverse("counter:activity", args=[self.counter.id])
- )
+ response = self.client.get(reverse("counter:activity", args=[self.counter.id]))
- self.assertFalse(
- '
S' Kia' in str(response_get.content)
- )
+ assert not '
S' Kia' in str(response.content)
class StudentCardTest(TestCase):
@@ -661,12 +594,16 @@ class StudentCardTest(TestCase):
Test that an user can be found with it's student card
"""
+ @classmethod
+ def setUpTestData(cls):
+ cls.krophil = User.objects.get(username="krophil")
+ cls.sli = User.objects.get(username="sli")
+ cls.skia = User.objects.get(username="skia")
+ cls.root = User.objects.get(username="root")
+
+ cls.counter = Counter.objects.get(id=2)
+
def setUp(self):
- self.krophil = User.objects.get(username="krophil")
- self.sli = User.objects.get(username="sli")
-
- self.counter = Counter.objects.filter(id=2).first()
-
# Auto login on counter
self.client.post(
reverse("counter:login", args=[self.counter.id]),
@@ -679,12 +616,9 @@ class StudentCardTest(TestCase):
{"code": "9A89B82018B0A0"},
)
- self.assertEqual(
- response.url,
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
+ assert response.url == reverse(
+ "counter:click",
+ kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
)
def test_add_student_card_from_counter(self):
@@ -780,7 +714,7 @@ class StudentCardTest(TestCase):
)
def test_delete_student_card_with_owner(self):
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
self.client.post(
reverse(
"counter:delete_student_card",
@@ -790,10 +724,10 @@ class StudentCardTest(TestCase):
},
)
)
- self.assertFalse(self.sli.customer.student_cards.exists())
+ assert not self.sli.customer.student_cards.exists()
def test_delete_student_card_with_board_member(self):
- self.client.login(username="skia", password="plop")
+ self.client.force_login(self.skia)
self.client.post(
reverse(
"counter:delete_student_card",
@@ -803,10 +737,10 @@ class StudentCardTest(TestCase):
},
)
)
- self.assertFalse(self.sli.customer.student_cards.exists())
+ assert not self.sli.customer.student_cards.exists()
def test_delete_student_card_with_root(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
self.client.post(
reverse(
"counter:delete_student_card",
@@ -816,10 +750,10 @@ class StudentCardTest(TestCase):
},
)
)
- self.assertFalse(self.sli.customer.student_cards.exists())
+ assert not self.sli.customer.student_cards.exists()
def test_delete_student_card_fail(self):
- self.client.login(username="krophil", password="plop")
+ self.client.force_login(self.krophil)
response = self.client.post(
reverse(
"counter:delete_student_card",
@@ -829,12 +763,12 @@ class StudentCardTest(TestCase):
},
)
)
- self.assertEqual(response.status_code, 403)
- self.assertTrue(self.sli.customer.student_cards.exists())
+ assert response.status_code == 403
+ assert self.sli.customer.student_cards.exists()
def test_add_student_card_from_user_preferences(self):
# Test with owner of the card
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
self.client.post(
reverse(
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
@@ -848,7 +782,7 @@ class StudentCardTest(TestCase):
self.assertContains(response, text="8B90734A802A8F")
# Test with board member
- self.client.login(username="skia", password="plop")
+ self.client.force_login(self.skia)
self.client.post(
reverse(
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
@@ -886,7 +820,7 @@ class StudentCardTest(TestCase):
self.assertContains(response, text="ABCAAAFAAFAAAB")
# Test with root
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
self.client.post(
reverse(
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
@@ -900,7 +834,7 @@ class StudentCardTest(TestCase):
self.assertContains(response, text="8B90734A802A8B")
def test_add_student_card_from_user_preferences_fail(self):
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
# UID too short
response = self.client.post(
reverse(
@@ -945,29 +879,30 @@ class StudentCardTest(TestCase):
reverse(
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
),
- {"uid": " "},
+ {"uid": " " * 14},
)
self.assertContains(response, text="Cet UID est invalide")
# Test with unauthorized user
- self.client.login(username="krophil", password="plop")
+ self.client.force_login(self.krophil)
response = self.client.post(
reverse(
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
),
{"uid": "8B90734A802A8F"},
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
class CustomerAccountIdTest(TestCase):
- def setUp(self):
- self.user_a = User.objects.create(
+ @classmethod
+ def setUpTestData(cls):
+ cls.user_a = User.objects.create(
username="a", password="plop", email="a.a@a.fr"
)
user_b = User.objects.create(username="b", password="plop", email="b.b@b.fr")
user_c = User.objects.create(username="c", password="plop", email="c.c@c.fr")
- Customer.objects.create(user=self.user_a, amount=10, account_id="1111a")
+ Customer.objects.create(user=cls.user_a, amount=10, account_id="1111a")
Customer.objects.create(user=user_b, amount=0, account_id="9999z")
Customer.objects.create(user=user_c, amount=0, account_id="12345f")
@@ -976,14 +911,14 @@ class CustomerAccountIdTest(TestCase):
customer, created = Customer.get_or_create(user_d)
account_id = customer.account_id
number = account_id[:-1]
- self.assertTrue(created)
- self.assertEqual(number, "12346")
- self.assertEqual(6, len(account_id))
- self.assertIn(account_id[-1], string.ascii_lowercase)
- self.assertEqual(0, customer.amount)
+ assert created is True
+ assert number == "12346"
+ assert 6 == len(account_id)
+ assert account_id[-1] in string.ascii_lowercase
+ assert customer.amount == 0
def test_get_existing_account(self):
account, created = Customer.get_or_create(self.user_a)
- self.assertFalse(created)
- self.assertEqual(account.account_id, "1111a")
- self.assertEqual(10, account.amount)
+ assert created is False
+ assert account.account_id == "1111a"
+ assert account.amount == 10
diff --git a/counter/views.py b/counter/views.py
index 6bbc819d..e27547d2 100644
--- a/counter/views.py
+++ b/counter/views.py
@@ -14,68 +14,65 @@
#
#
import json
+import re
+from datetime import datetime, timedelta
+from http import HTTPStatus
from urllib.parse import parse_qs
+import pytz
+from django import forms
+from django.conf import settings
from django.contrib.auth.decorators import login_required
-from django.db.models import F
-from django.shortcuts import get_object_or_404
-from django.http import Http404
from django.core.exceptions import PermissionDenied
+from django.db import DataError, transaction
+from django.db.models import F
+from django.forms import CheckboxSelectMultiple
+from django.forms.models import modelform_factory
+from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
+from django.shortcuts import get_object_or_404
+from django.urls import reverse, reverse_lazy
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST
-from django.views.generic import ListView, DetailView, RedirectView, TemplateView
+from django.views.generic import DetailView, ListView, RedirectView, TemplateView
from django.views.generic.base import View
from django.views.generic.edit import (
- UpdateView,
CreateView,
DeleteView,
- ProcessFormView,
FormMixin,
FormView,
+ ProcessFormView,
+ UpdateView,
)
-from django.forms.models import modelform_factory
-from django.forms import CheckboxSelectMultiple
-from django.urls import reverse_lazy, reverse
-from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
-from django.utils import timezone
-from django import forms
-from django.utils.translation import gettext_lazy as _
-from django.conf import settings
-from django.db import DataError, transaction
-import json
-import re
-import pytz
-from datetime import timedelta, datetime
-from http import HTTPStatus
-
-from core.utils import get_start_of_semester, get_semester_code
-from core.views import CanViewMixin, TabedViewMixin, CanEditMixin
-from core.views.forms import LoginForm
+from accounting.models import CurrencyField
from core.models import User
+from core.utils import get_semester_code, get_start_of_semester
+from core.views import CanEditMixin, CanViewMixin, TabedViewMixin
+from core.views.forms import LoginForm
from counter.forms import (
BillingInfoForm,
- StudentCardForm,
- GetUserForm,
- RefillForm,
- CounterEditForm,
- ProductEditForm,
CashSummaryFormBase,
+ CounterEditForm,
EticketForm,
+ GetUserForm,
+ ProductEditForm,
+ RefillForm,
+ StudentCardForm,
)
from counter.models import (
- Counter,
- Customer,
- StudentCard,
- Product,
- Selling,
- Refilling,
- ProductType,
+ BillingInfo,
CashRegisterSummary,
CashRegisterSummaryItem,
+ Counter,
+ Customer,
Eticket,
- BillingInfo,
+ Product,
+ ProductType,
+ Refilling,
+ Selling,
+ StudentCard,
)
-from accounting.models import CurrencyField
class CounterAdminMixin(View):
@@ -141,9 +138,11 @@ class CounterTabsMixin(TabedViewMixin):
"url": reverse_lazy(
"counter:details",
kwargs={
- "counter_id": self.object.stock_owner.counter.id
- if hasattr(self.object, "stock_owner")
- else self.object.id
+ "counter_id": (
+ self.object.stock_owner.counter.id
+ if hasattr(self.object, "stock_owner")
+ else self.object.id
+ )
},
),
"slug": "counter",
@@ -160,9 +159,11 @@ class CounterTabsMixin(TabedViewMixin):
"url": reverse_lazy(
"counter:cash_summary",
kwargs={
- "counter_id": self.object.stock_owner.counter.id
- if hasattr(self.object, "stock_owner")
- else self.object.id
+ "counter_id": (
+ self.object.stock_owner.counter.id
+ if hasattr(self.object, "stock_owner")
+ else self.object.id
+ )
},
),
"slug": "cash_summary",
@@ -174,9 +175,11 @@ class CounterTabsMixin(TabedViewMixin):
"url": reverse_lazy(
"counter:last_ops",
kwargs={
- "counter_id": self.object.stock_owner.counter.id
- if hasattr(self.object, "stock_owner")
- else self.object.id
+ "counter_id": (
+ self.object.stock_owner.counter.id
+ if hasattr(self.object, "stock_owner")
+ else self.object.id
+ )
},
),
"slug": "last_ops",
@@ -189,9 +192,11 @@ class CounterTabsMixin(TabedViewMixin):
"url": reverse_lazy(
"stock:take_items",
kwargs={
- "stock_id": self.object.stock.id
- if hasattr(self.object, "stock")
- else self.object.stock_owner.id
+ "stock_id": (
+ self.object.stock.id
+ if hasattr(self.object, "stock")
+ else self.object.stock_owner.id
+ )
},
),
"slug": "take_items_from_stock",
@@ -1199,35 +1204,35 @@ class CashRegisterSummaryForm(forms.Form):
cash_summary=summary,
value=cd["check_1_value"],
quantity=cd["check_1_quantity"],
- check=True,
+ is_check=True,
).save()
if cd["check_2_quantity"]:
CashRegisterSummaryItem(
cash_summary=summary,
value=cd["check_2_value"],
quantity=cd["check_2_quantity"],
- check=True,
+ is_check=True,
).save()
if cd["check_3_quantity"]:
CashRegisterSummaryItem(
cash_summary=summary,
value=cd["check_3_value"],
quantity=cd["check_3_quantity"],
- check=True,
+ is_check=True,
).save()
if cd["check_4_quantity"]:
CashRegisterSummaryItem(
cash_summary=summary,
value=cd["check_4_value"],
quantity=cd["check_4_quantity"],
- check=True,
+ is_check=True,
).save()
if cd["check_5_quantity"]:
CashRegisterSummaryItem(
cash_summary=summary,
value=cd["check_5_value"],
quantity=cd["check_5_quantity"],
- check=True,
+ is_check=True,
).save()
if summary.items.count() < 1:
summary.delete()
@@ -1478,7 +1483,7 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
end_date = (start_date + timedelta(days=32)).replace(
day=1, hour=0, minute=0, microsecond=0
)
- from django.db.models import Sum, Case, When, F
+ from django.db.models import Case, F, Sum, When
kwargs["sum_cb"] = sum(
[
@@ -1566,12 +1571,12 @@ class EticketPDFView(CanViewMixin, DetailView):
pk_url_kwarg = "selling_id"
def get(self, request, *args, **kwargs):
- from reportlab.pdfgen import canvas
- from reportlab.lib.utils import ImageReader
- from reportlab.lib.units import cm
- from reportlab.graphics.shapes import Drawing
- from reportlab.graphics.barcode.qr import QrCodeWidget
from reportlab.graphics import renderPDF
+ from reportlab.graphics.barcode.qr import QrCodeWidget
+ from reportlab.graphics.shapes import Drawing
+ from reportlab.lib.units import cm
+ from reportlab.lib.utils import ImageReader
+ from reportlab.pdfgen import canvas
if not (
hasattr(self.object, "product") and hasattr(self.object.product, "eticket")
diff --git a/doc/conf.py b/doc/conf.py
index 2a103e0b..d9f219c9 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -12,6 +12,7 @@
#
import os
import sys
+
import django
sys.path.insert(0, os.path.abspath(".."))
diff --git a/doc/start/devtools.rst b/doc/start/devtools.rst
index 9256bfb4..1cc9dc3a 100644
--- a/doc/start/devtools.rst
+++ b/doc/start/devtools.rst
@@ -1,70 +1,112 @@
Configurer son environnement de développement
=============================================
-Le projet n'est en aucun cas lié à un quelconque environnement de développement. Il est possible pour chacun de travailler avec les outils dont il a envie et d'utiliser l'éditeur de code avec lequel il est le plus à l'aise.
+Le projet n'est en aucun cas lié à un quelconque environnement de développement.
+Il est possible pour chacun de travailler avec les outils dont il a envie et d'utiliser l'éditeur de code avec lequel il est le plus à l'aise.
Pour donner une idée, Skia a écrit une énorme partie de projet avec l'éditeur *Vim* sur du GNU/Linux
alors que Sli a utilisé *Sublime Text* sur MacOS et que Maréchal travaille avec PyCharm
-sur Windows muni de WSL.
+sur ~~Windows muni de WSL~~ Arch Linux btw.
-Configurer Black pour son éditeur
+Configurer les pre-commit hooks
+--------------------------------
+
+La procédure habituelle pour contribuer au projet consiste à commit des modifications, puis à les push sur le dépôt distant et à ouvrir une pull request. Cette PR va faire tourner les outils de vérification de la qualité de code. Si la vérification échoue, la PR est bloquée, et il faut réparer le problème (ce qui implique de push un micro-commit ou de push force sur la branche).
+
+Dans l'idéal, on aimerait donc qu'il soit impossible d'oublier de faire tourner ces vérification. Pour ça, il existe un mécanisme : les pre-commits hooks. Ce sont des actions qui tournent automatiquement lorsque vous effectuez un `git commit`. Ces dernières vont analyser et éventuellement modifier le code, avant que Git n'ajoute effectivement le commit sur l'arbre git. Voyez ça comme une micro-CI qui tourne en local.
+
+Les git hooks sont une fonctionnalité par défaut de Git. Cependant, leur configuration peut-être un peu embêtante si vous le faites manuellement. Pour gérer ça plus simplement, nous utilisons le logiciel python [pre-commit](https://pre-commit.com/) qui permet de contrôler leur installation via un seul fichier de configuration, placé à la racine du projet (plus précisément, il s'agit du fichier `.pre-commit-config.yaml`).
+
+.. note::
+
+ Les pre-commits sont également utilisés dans la CI. Si ces derniers fonctionnent localement, vous avez la garantie que la pipeline ne sera pas fachée. ;)
+
+C'est une fonctionnalité de git lui même mais c'est assez embêtant à gérer manuellement. Pour gérer ça plus simplement, nous utilisons le logiciel python [pre-commit](https://pre-commit.com/) qui permet de contrôller leur installation via un fichier yaml.
+
+Les pre-commits sont également utilisés dans la CI, si les pre-commits fonctionnent localement, vous avez la garantie que la pipeline ne sera pas fachée ;).
+
+Le logiciel est installé par défaut par poetry. Il suffit ensuite de lancer :
+
+.. code-block::
+
+ pre-commit install
+
+Tout se fait ensuite automatiquement lorsqu'on utilise git normalement pour commit. Pour appliquer les pre-commits manuellement, il est possible d'appeler soi même les pre-commits
+
+.. code-block::
+
+ pre-commit run --all-files
+
+
+Configurer Ruff pour son éditeur
---------------------------------
.. note::
- Black est inclus dans les dépendances du projet.
+ Ruff est inclus dans les dépendances du projet.
Si vous avez réussi à terminer l'installation, vous n'avez donc pas de configuration
supplémentaire à effectuer.
-Pour utiliser Black, placez-vous à la racine du projet et lancez la commande suivante :
+Pour utiliser Ruff, placez-vous à la racine du projet et lancez la commande suivante :
.. code-block::
- black .
+ ruff format # pour formatter le code
+ ruff check # pour linter le code
-Black va alors faire son travail sur l'ensemble du projet puis vous dire quels documents
-ont été reformatés.
+Ruff va alors faire son travail sur l'ensemble du projet puis vous dire
+si des documents ont été reformatés (si vous avez fait `ruff format`)
+ou bien s'il y a des erreurs à réparer (si vous avez faire `ruff check`).
-Appeler Black en ligne de commandes avant de pousser votre code sur Github
+Appeler Ruff en ligne de commandes avant de pousser votre code sur Github
est une technique qui marche très bien.
Cependant, vous risquez de souvent l'oublier.
-Or, lorsque le code est mal formaté, la pipeline bloque les PR sur les branches protégées.
+Or, lorsque le code ne respecte pas les standards de qualité,
+la pipeline bloque les PR sur les branches protégées.
-Pour éviter de vous faire régulièrement blacked, vous pouvez configurer
-votre éditeur pour que Black fasse son travail automatiquement à chaque édition d'un fichier.
+Pour éviter de vous faire régulièrement avoir, vous pouvez configurer
+votre éditeur pour que Ruff fasse son travail automatiquement à chaque édition d'un fichier.
Nous tenterons de vous faire ici un résumé pour deux éditeurs de textes populaires
que sont VsCode et Sublime Text.
VsCode
~~~~~~
-.. warning::
-
- Il faut installer black dans son environement virtuel pour cet éditeur
-
-Black est directement pris en charge par l'extension pour le Python de VsCode, il suffit de rentrer la configuration suivante :
+Installez l'extension Ruff pour VsCode.
+Ensuite, ajoutez ceci dans votre configuration :
.. sourcecode:: json
{
- "python.formatting.provider": "black",
- "editor.formatOnSave": true
+ "[python]": {
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "charliermarsh.ruff"
+ }
}
Sublime Text
~~~~~~~~~~~~
-Il est tout d'abord nécessaire d'installer ce plugin : https://packagecontrol.io/packages/sublack.
+Vous devez installer ce plugin : https://packagecontrol.io/packages/LSP-ruff.
+Suivez ensuite les instructions données dans la description du plugin.
-Il suffit ensuite d'ajouter dans les settings du projet (ou directement dans les settings globales) :
+Dans la configuration de votre projet, ajoutez ceci:
.. sourcecode:: json
{
- "sublack.black_on_save": true
+ "settings": {
+ "lsp_format_on_save": true,
+ "LSP": {
+ "LSP-ruff": {
+ "enabled": true,
+ }
+ }
+ }
}
-Si vous utilisez le plugin `anaconda
`__, pensez à modifier les paramètres du linter pep8 pour éviter de recevoir des warnings dans le formatage de black comme ceci :
+
+Si vous utilisez le plugin `anaconda
`__, pensez à modifier les paramètres du linter pep8 pour éviter de recevoir des warnings dans le formatage de ruff comme ceci :
.. sourcecode:: json
diff --git a/doc/start/install.rst b/doc/start/install.rst
index 0e97ab15..257fcf46 100644
--- a/doc/start/install.rst
+++ b/doc/start/install.rst
@@ -9,7 +9,6 @@ Certaines dépendances sont nécessaires niveau système :
* poetry
* libssl
* libjpeg
-* libxapian-dev
* zlib1g-dev
* python
* gettext
@@ -76,13 +75,13 @@ Sur Ubuntu
# Sait-on jamais
sudo apt update
- sudo apt install python-is-python3 # Permet d'utiliser python au lieu de python3, c'est optionel
+ sudo apt install python-is-python3 # Permet d'utiliser python au lieu de python3, c'est optionnel
sudo apt install build-essentials libssl-dev libjpeg-dev zlib1g-dev python-dev \
- libffi-dev python-dev-is-python3 libgraphviz-dev pkg-config libxapian-dev \
- gettext git
+ libffi-dev python-dev-is-python3 libgraphviz-dev pkg-config \
+ gettext git pipx
- curl -sSL https://install.python-poetry.org | python -
+ pipx install poetry
.. note::
@@ -92,22 +91,21 @@ Sur Ubuntu
Sur MacOS
~~~~~~~~~
-Pour installer les dépendances, il est fortement recommandé d'installer le gestionnaire de paquets `homebrew
`_.
+Pour installer les dépendances, il est fortement recommandé d'installer le gestionnaire de paquets `homebrew `_.
+Il est également nécessaire d'avoir installé xcode
.. sourcecode:: bash
- brew install git python xapian graphviz poetry
-
- # Si vous aviez une version de python ne venant pas de homebrew
- brew link --overwrite python
+ echo 'export PATH="$(brew --prefix graphviz)/bin:$PATH"' >> ~/.zshrc
+ echo 'export CFLAGS="-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -I $(brew --prefix graphviz)/include"' >> ~/.zshrc
+ echo 'export LDFLAGS="-L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -L $(brew --prefix graphviz)/lib"' >> ~/.zshrc
+ brew install git python graphviz pipx
+ pipx install poetry
# Pour bien configurer gettext
brew link gettext # (suivez bien les instructions supplémentaires affichées)
- # Pour installer poetry
- pip3 install poetry
-
.. note::
Si vous rencontrez des erreurs lors de votre configuration, n'hésitez pas à vérifier l'état de votre installation homebrew avec :code:`brew doctor`
@@ -134,6 +132,9 @@ Finaliser l'installation
# Activation de l'environnement virtuel
poetry shell
+ # Installe xapian
+ python manage.py install_xapian
+
# Prépare la base de données
python manage.py setup
@@ -178,7 +179,7 @@ Cette commande génère la documentation à chacune de ses modifications, inutil
Les dépendances pour la documentation sont optionnelles.
Avant de commencer à travailler sur la doc, il faut donc les installer
- avec la commande :code:`poetry install -E docs`
+ avec la commande :code:`poetry install --with docs`
.. sourcecode:: bash
@@ -195,16 +196,39 @@ Pour lancer les tests il suffit d'utiliser la commande intégrée à django.
.. code-block:: bash
# Lancer tous les tests
- python manage.py test
+ pytest
# Lancer les tests de l'application core
- python manage.py test core
+ pytest core
# Lancer les tests de la classe UserRegistrationTest de core
- python manage.py test core.tests.UserRegistrationTest
+ pytest core.tests.UserRegistrationTest
# Lancer une méthode en particulier de cette même classe
- python manage.py test core.tests.UserRegistrationTest.test_register_user_form_ok
+ pytest core.tests.UserRegistrationTest.test_register_user_form_ok
+
+.. note::
+
+ Certains tests sont un peu longs à tourner.
+ Pour ne faire tourner que les tests les plus rapides,
+ vous pouvez exécutez pytest ainsi :
+
+ .. code-block:: bash
+
+ pytest -m "not slow"
+
+ # vous pouvez toujours faire comme au-dessus
+ pytest core -m "not slow"
+
+ A l'inverse, vous pouvez ne faire tourner que les tests
+ lents en remplaçant `-m "not slow"` par `-m slow`.
+
+ De cette manière, votre processus de développement
+ devrait être un peu plus fluide.
+ Cependant, n'oubliez pas de bien faire tourner
+ tous les tests avant de push un commit.
+
+
Vérifier les dépendances Javascript
-----------------------------------
diff --git a/eboutic/forms.py b/eboutic/forms.py
index c5842700..3189e330 100644
--- a/eboutic/forms.py
+++ b/eboutic/forms.py
@@ -25,8 +25,8 @@
import json
import re
import typing
-
from urllib.parse import unquote
+
from django.http import HttpRequest
from django.utils.translation import gettext as _
from sentry_sdk import capture_message
diff --git a/eboutic/migrations/0001_initial.py b/eboutic/migrations/0001_initial.py
index b642642f..f698999c 100644
--- a/eboutic/migrations/0001_initial.py
+++ b/eboutic/migrations/0001_initial.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-import accounting.models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+import accounting.models
class Migration(migrations.Migration):
diff --git a/eboutic/models.py b/eboutic/models.py
index 556eb8ab..da048b63 100644
--- a/eboutic/models.py
+++ b/eboutic/models.py
@@ -14,21 +14,20 @@
#
#
import hmac
-import html
import typing
from datetime import datetime
from typing import List
from dict2xml import dict2xml
from django.conf import settings
-from django.db import models, DataError
-from django.db.models import Sum, F
+from django.db import DataError, models
+from django.db.models import F, Sum
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from accounting.models import CurrencyField
-from core.models import Group, User
-from counter.models import Counter, Product, Selling, Refilling, BillingInfo, Customer
+from core.models import User
+from counter.models import BillingInfo, Counter, Customer, Product, Refilling, Selling
def get_eboutic_products(user: User) -> List[Product]:
diff --git a/eboutic/templates/eboutic/eboutic_main.jinja b/eboutic/templates/eboutic/eboutic_main.jinja
index 8a7cd6d6..9a8470d5 100644
--- a/eboutic/templates/eboutic/eboutic_main.jinja
+++ b/eboutic/templates/eboutic/eboutic_main.jinja
@@ -79,9 +79,7 @@
{% if not request.user.date_of_birth %}
- {% trans %}You have not filled in your date of birth. As a result, you may not have access to
- all the products in the online shop. To fill in your date of birth, you can go
- to{% endtrans %}
+ {% trans %}You have not filled in your date of birth. As a result, you may not have access to all the products in the online shop. To fill in your date of birth, you can go to{% endtrans %}
{% trans %}this page{% endtrans %}
@@ -92,24 +90,6 @@
{% endif %}
-
-
{% for priority_groups in products|groupby('priority')|reverse %}
{% for category, items in priority_groups.list|groupby('category') %}
{% if items|count > 0 %}
diff --git a/eboutic/tests.py b/eboutic/tests.py
index af0e5850..d99c8536 100644
--- a/eboutic/tests.py
+++ b/eboutic/tests.py
@@ -26,29 +26,31 @@ import base64
import json
import urllib
-from OpenSSL import crypto
+from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
+from cryptography.hazmat.primitives.hashes import SHA1
+from cryptography.hazmat.primitives.serialization import load_pem_private_key
from django.conf import settings
-from django.core.management import call_command
from django.db.models import Max
from django.test import TestCase
from django.urls import reverse
from core.models import User
-from counter.models import Product, Counter, Customer, Selling
+from counter.models import Counter, Customer, Product, Selling
from eboutic.models import Basket
class EbouticTest(TestCase):
@classmethod
def setUpTestData(cls):
- cls.barbar = Product.objects.filter(code="BARB").first()
- cls.refill = Product.objects.filter(code="15REFILL").first()
- cls.cotis = Product.objects.filter(code="1SCOTIZ").first()
- cls.eboutic = Counter.objects.filter(name="Eboutic").first()
- cls.skia = User.objects.filter(username="skia").first()
- cls.subscriber = User.objects.filter(username="subscriber").first()
- cls.old_subscriber = User.objects.filter(username="old_subscriber").first()
- cls.public = User.objects.filter(username="public").first()
+ cls.barbar = Product.objects.get(code="BARB")
+ cls.refill = Product.objects.get(code="15REFILL")
+ cls.cotis = Product.objects.get(code="1SCOTIZ")
+ cls.eboutic = Counter.objects.get(name="Eboutic")
+ cls.skia = User.objects.get(username="skia")
+ cls.subscriber = User.objects.get(username="subscriber")
+ cls.old_subscriber = User.objects.get(username="old_subscriber")
+ cls.public = User.objects.get(username="public")
def get_busy_basket(self, user) -> Basket:
"""
@@ -68,12 +70,12 @@ class EbouticTest(TestCase):
basket_id = basket.id
amount = int(basket.get_total() * 100)
query = f"Amount={amount}&BasketID={basket_id}&Auto=42&Error=00000"
- with open("./eboutic/tests/private_key.pem") as f:
+ with open("./eboutic/tests/private_key.pem", "br") as f:
PRIVKEY = f.read()
with open("./eboutic/tests/public_key.pem") as f:
settings.SITH_EBOUTIC_PUB_KEY = f.read()
- privkey = crypto.load_privatekey(crypto.FILETYPE_PEM, PRIVKEY)
- sig = crypto.sign(privkey, query.encode("utf-8"), "sha1")
+ key: RSAPrivateKey = load_pem_private_key(PRIVKEY, None)
+ sig = key.sign(query.encode("utf-8"), PKCS1v15(), SHA1())
b64sig = base64.b64encode(sig).decode("ascii")
url = reverse("eboutic:etransation_autoanswer") + "?%s&Sig=%s" % (
@@ -83,7 +85,7 @@ class EbouticTest(TestCase):
return url
def test_buy_with_sith_account(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
self.subscriber.customer.amount = 100 # give money before test
self.subscriber.customer.save()
basket = self.get_busy_basket(self.subscriber)
@@ -91,14 +93,12 @@ class EbouticTest(TestCase):
response = self.client.post(reverse("eboutic:pay_with_sith"))
self.assertRedirects(response, "/eboutic/pay/success/")
new_balance = Customer.objects.get(user=self.subscriber).amount
- self.assertEqual(float(new_balance), 100 - amount)
- self.assertEqual(
- 'basket_items=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/eboutic',
- self.client.cookies["basket_items"].OutputString(),
- )
+ assert float(new_balance) == 100 - amount
+ expected = 'basket_items=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/eboutic'
+ assert expected == self.client.cookies["basket_items"].OutputString()
def test_buy_with_sith_account_no_money(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
basket = self.get_busy_basket(self.subscriber)
initial = basket.get_total() - 1 # just not enough to complete the sale
self.subscriber.customer.amount = initial
@@ -106,22 +106,19 @@ class EbouticTest(TestCase):
response = self.client.post(reverse("eboutic:pay_with_sith"))
self.assertRedirects(response, "/eboutic/pay/failure/")
new_balance = Customer.objects.get(user=self.subscriber).amount
- self.assertEqual(float(new_balance), initial)
- self.assertEqual(
- 'basket_items=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/eboutic',
- self.client.cookies["basket_items"].OutputString(),
- ) # this cookie should be removed after payment
+ assert float(new_balance) == initial
+ # this cookie should be removed after payment
+ expected = 'basket_items=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/eboutic'
+ assert expected == self.client.cookies["basket_items"].OutputString()
def test_submit_basket(self):
- self.client.login(username="subscriber", password="plop")
- self.client.cookies[
- "basket_items"
- ] = """[
+ self.client.force_login(self.subscriber)
+ self.client.cookies["basket_items"] = """[
{"id": 2, "name": "Cotis 2 semestres", "quantity": 1, "unit_price": 28},
{"id": 4, "name": "Barbar", "quantity": 3, "unit_price": 1.7}
]"""
response = self.client.get(reverse("eboutic:command"))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
self.assertInHTML(
"Cotis 2 semestres | 1 | 28.00 € |
",
response.content.decode(),
@@ -130,61 +127,50 @@ class EbouticTest(TestCase):
"Barbar | 3 | 1.70 € |
",
response.content.decode(),
)
- self.assertIn("basket_id", self.client.session)
+ assert "basket_id" in self.client.session
basket = Basket.objects.get(id=self.client.session["basket_id"])
- self.assertEqual(basket.items.count(), 2)
+ assert basket.items.count() == 2
barbar = basket.items.filter(product_name="Barbar").first()
- self.assertIsNotNone(barbar)
- self.assertEqual(barbar.quantity, 3)
+ assert barbar is not None
+ assert barbar.quantity == 3
cotis = basket.items.filter(product_name="Cotis 2 semestres").first()
- self.assertIsNotNone(cotis)
- self.assertEqual(cotis.quantity, 1)
- self.assertEqual(basket.get_total(), 3 * 1.7 + 28)
+ assert cotis is not None
+ assert cotis.quantity == 1
+ assert basket.get_total() == 3 * 1.7 + 28
def test_submit_empty_basket(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
self.client.cookies["basket_items"] = "[]"
response = self.client.get(reverse("eboutic:command"))
self.assertRedirects(response, "/eboutic/")
def test_submit_invalid_basket(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
max_id = Product.objects.aggregate(res=Max("id"))["res"]
- self.client.cookies[
- "basket_items"
- ] = f"""[
+ self.client.cookies["basket_items"] = f"""[
{{"id": {max_id + 1}, "name": "", "quantity": 1, "unit_price": 28}}
]"""
response = self.client.get(reverse("eboutic:command"))
- self.assertIn(
- 'basket_items=""',
- self.client.cookies["basket_items"].OutputString(),
- )
- self.assertIn(
- "Path=/eboutic",
- self.client.cookies["basket_items"].OutputString(),
- )
+ cookie = self.client.cookies["basket_items"].OutputString()
+ assert 'basket_items=""' in cookie
+ assert "Path=/eboutic" in cookie
self.assertRedirects(response, "/eboutic/")
def test_submit_basket_illegal_quantity(self):
- self.client.login(username="subscriber", password="plop")
- self.client.cookies[
- "basket_items"
- ] = """[
+ self.client.force_login(self.subscriber)
+ self.client.cookies["basket_items"] = """[
{"id": 4, "name": "Barbar", "quantity": -1, "unit_price": 1.7}
]"""
response = self.client.get(reverse("eboutic:command"))
self.assertRedirects(response, "/eboutic/")
def test_buy_subscribe_product_with_credit_card(self):
- self.client.login(username="old_subscriber", password="plop")
+ self.client.force_login(self.old_subscriber)
response = self.client.get(
reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id})
)
- self.assertTrue("Non cotisant" in str(response.content))
- self.client.cookies[
- "basket_items"
- ] = """[
+ assert "Non cotisant" in str(response.content)
+ self.client.cookies["basket_items"] = """[
{"id": 2, "name": "Cotis 2 semestres", "quantity": 1, "unit_price": 28}
]"""
response = self.client.get(reverse("eboutic:command"))
@@ -193,21 +179,21 @@ class EbouticTest(TestCase):
response.content.decode(),
)
basket = Basket.objects.get(id=self.client.session["basket_id"])
- self.assertEqual(basket.items.count(), 1)
+ assert basket.items.count() == 1
response = self.client.get(self.generate_bank_valid_answer())
- self.assertTrue(response.status_code == 200)
- self.assertTrue(response.content.decode("utf-8") == "Payment successful")
+ assert response.status_code == 200
+ assert response.content.decode("utf-8") == "Payment successful"
subscriber = User.objects.get(id=self.old_subscriber.id)
- self.assertEqual(subscriber.subscriptions.count(), 2)
+ assert subscriber.subscriptions.count() == 2
sub = subscriber.subscriptions.order_by("-subscription_end").first()
- self.assertTrue(sub.is_valid_now())
- self.assertEqual(sub.member, subscriber)
- self.assertEqual(sub.subscription_type, "deux-semestres")
- self.assertEqual(sub.location, "EBOUTIC")
+ assert sub.is_valid_now()
+ assert sub.member == subscriber
+ assert sub.subscription_type == "deux-semestres"
+ assert sub.location == "EBOUTIC"
def test_buy_refill_product_with_credit_card(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
# basket contains 1 refill item worth 15€
self.client.cookies["basket_items"] = json.dumps(
[{"id": 3, "name": "Rechargement 15 €", "quantity": 1, "unit_price": 15}]
@@ -217,13 +203,13 @@ class EbouticTest(TestCase):
url = self.generate_bank_valid_answer()
response = self.client.get(url)
- self.assertTrue(response.status_code == 200)
- self.assertTrue(response.content.decode() == "Payment successful")
+ assert response.status_code == 200
+ assert response.content.decode() == "Payment successful"
new_balance = Customer.objects.get(user=self.subscriber).amount
- self.assertEqual(new_balance, initial_balance + 15)
+ assert new_balance == initial_balance + 15
def test_alter_basket_after_submission(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
self.client.cookies["basket_items"] = json.dumps(
[{"id": 4, "name": "Barbar", "quantity": 1, "unit_price": 1.7}]
)
@@ -236,30 +222,31 @@ class EbouticTest(TestCase):
)
self.client.get(reverse("eboutic:command"))
response = self.client.get(et_answer_url)
- self.assertEqual(response.status_code, 500)
- self.assertIn(
- "Basket processing failed with error: SuspiciousOperation('Basket total and amount do not match'",
- response.content.decode("utf-8"),
+ assert response.status_code == 500
+ msg = (
+ "Basket processing failed with error: "
+ "SuspiciousOperation('Basket total and amount do not match'"
)
+ assert msg in response.content.decode("utf-8")
def test_buy_simple_product_with_credit_card(self):
- self.client.login(username="subscriber", password="plop")
+ self.client.force_login(self.subscriber)
self.client.cookies["basket_items"] = json.dumps(
[{"id": 4, "name": "Barbar", "quantity": 1, "unit_price": 1.7}]
)
self.client.get(reverse("eboutic:command"))
et_answer_url = self.generate_bank_valid_answer()
response = self.client.get(et_answer_url)
- self.assertTrue(response.status_code == 200)
- self.assertTrue(response.content.decode("utf-8") == "Payment successful")
+ assert response.status_code == 200
+ assert response.content.decode("utf-8") == "Payment successful"
selling = (
Selling.objects.filter(customer=self.subscriber.customer)
.order_by("-date")
.first()
)
- self.assertEqual(selling.payment_method, "CARD")
- self.assertEqual(selling.quantity, 1)
- self.assertEqual(selling.unit_price, self.barbar.selling_price)
- self.assertEqual(selling.counter.type, "EBOUTIC")
- self.assertEqual(selling.product, self.barbar)
+ assert selling.payment_method == "CARD"
+ assert selling.quantity == 1
+ assert selling.unit_price == self.barbar.selling_price
+ assert selling.counter.type == "EBOUTIC"
+ assert selling.product == self.barbar
diff --git a/eboutic/tests/test.py b/eboutic/tests/test.py
index 2a4e6cfb..5527f116 100755
--- a/eboutic/tests/test.py
+++ b/eboutic/tests/test.py
@@ -7,28 +7,34 @@
#
import base64
-from OpenSSL import crypto
-with open("./private_key.pem") as f:
- PRVKEY = f.read()
-with open("./public_key.pem") as f:
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
+from cryptography.hazmat.primitives.hashes import SHA1
+from cryptography.hazmat.primitives.serialization import (
+ load_pem_private_key,
+ load_pem_public_key,
+)
+
+with open("./private_key.pem", "br") as f:
+ PRIVKEY = f.read()
+with open("./public_key.pem", "br") as f:
PUBKEY = f.read()
data = "Amount=400&BasketID=4000&Auto=42&Error=00000\n".encode("utf-8")
# Sign
-prvkey = crypto.load_privatekey(crypto.FILETYPE_PEM, PRVKEY)
-sig = crypto.sign(prvkey, data, "sha1")
-b64sig = base64.b64encode(sig)
+privkey: RSAPrivateKey = load_pem_private_key(PRIVKEY, None)
+signature = privkey.sign(data, PKCS1v15(), SHA1())
+b64sig = base64.b64encode(signature)
print(b64sig)
# Verify
-pubkey = crypto.load_publickey(crypto.FILETYPE_PEM, PUBKEY)
-cert = crypto.X509()
-cert.set_pubkey(pubkey)
-sig = base64.b64decode(b64sig)
+pubkey = load_pem_public_key(PUBKEY)
+signature = base64.b64decode(b64sig)
try:
- crypto.verify(cert, sig, data, "sha1")
+ pubkey.verify(signature, data, PKCS1v15(), SHA1())
print("Verify OK")
-except:
+except InvalidSignature as e:
print("Verify failed")
diff --git a/eboutic/urls.py b/eboutic/urls.py
index a1bd6ecd..22602aeb 100644
--- a/eboutic/urls.py
+++ b/eboutic/urls.py
@@ -25,8 +25,8 @@
from django.urls import path, register_converter
-from eboutic.views import *
from eboutic.converters import PaymentResultConverter
+from eboutic.views import *
register_converter(PaymentResultConverter, "res")
diff --git a/eboutic/views.py b/eboutic/views.py
index 885edf43..8c39bf64 100644
--- a/eboutic/views.py
+++ b/eboutic/views.py
@@ -16,23 +16,27 @@
import base64
import json
-import sentry_sdk
-
from datetime import datetime
from urllib.parse import unquote
-from OpenSSL import crypto
+
+import sentry_sdk
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
+from cryptography.hazmat.primitives.hashes import SHA1
+from cryptography.hazmat.primitives.serialization import load_pem_public_key
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import SuspiciousOperation
-from django.db import transaction, DatabaseError
-from django.http import HttpResponse, HttpRequest
-from django.shortcuts import render, redirect
+from django.db import DatabaseError, transaction
+from django.http import HttpRequest, HttpResponse
+from django.shortcuts import redirect, render
from django.utils.decorators import method_decorator
from django.views.decorators.http import require_GET, require_POST
from django.views.generic import TemplateView, View
from counter.forms import BillingInfoForm
-from counter.models import Customer, Counter, Product
+from counter.models import Counter, Customer, Product
from eboutic.forms import BasketForm
from eboutic.models import Basket, Invoice, InvoiceItem, get_eboutic_products
@@ -180,18 +184,14 @@ class EtransactionAutoAnswer(View):
required = {"Amount", "BasketID", "Error", "Sig"}
if not required.issubset(set(request.GET.keys())):
return HttpResponse("Bad arguments", status=400)
- key = crypto.load_publickey(crypto.FILETYPE_PEM, settings.SITH_EBOUTIC_PUB_KEY)
- cert = crypto.X509()
- cert.set_pubkey(key)
- sig = base64.b64decode(request.GET["Sig"])
+ pubkey: RSAPublicKey = load_pem_public_key(
+ settings.SITH_EBOUTIC_PUB_KEY.encode("utf-8")
+ )
+ signature = base64.b64decode(request.GET["Sig"])
try:
- crypto.verify(
- cert,
- sig,
- "&".join(request.META["QUERY_STRING"].split("&")[:-1]).encode("utf-8"),
- "sha1",
- )
- except:
+ data = "&".join(request.META["QUERY_STRING"].split("&")[:-1])
+ pubkey.verify(signature, data.encode("utf-8"), PKCS1v15(), SHA1())
+ except InvalidSignature:
return HttpResponse("Bad signature", status=400)
# Payment authorized:
# * 'Error' is '00000'
diff --git a/election/admin.py b/election/admin.py
index 3cd3ce37..fadc9a52 100644
--- a/election/admin.py
+++ b/election/admin.py
@@ -1,7 +1,7 @@
from ajax_select import make_ajax_form
from django.contrib import admin
-from election.models import Election, Role, ElectionList, Candidature
+from election.models import Candidature, Election, ElectionList, Role
@admin.register(Election)
diff --git a/election/migrations/0001_initial.py b/election/migrations/0001_initial.py
index d3c4c449..04deb469 100644
--- a/election/migrations/0001_initial.py
+++ b/election/migrations/0001_initial.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/election/models.py b/election/models.py
index f100a1d6..3bcf761f 100644
--- a/election/models.py
+++ b/election/models.py
@@ -1,9 +1,9 @@
from django.db import models
-from ordered_model.models import OrderedModel
-from django.utils.translation import gettext_lazy as _
from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+from ordered_model.models import OrderedModel
-from core.models import User, Group
+from core.models import Group, User
class Election(models.Model):
diff --git a/election/tests.py b/election/tests.py
index 03b46481..9ea534b8 100644
--- a/election/tests.py
+++ b/election/tests.py
@@ -1,62 +1,49 @@
+from django.conf import settings
from django.test import TestCase
from django.urls import reverse
-from django.core.management import call_command
-from django.conf import settings
-from core.models import User, Group
+from core.models import Group, User
from election.models import Election
-class MainElection(TestCase):
- def setUp(self):
- self.election = Election.objects.all().first()
- self.public_group = Group.objects.get(id=settings.SITH_GROUP_PUBLIC_ID)
- self.subscriber_group = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
- self.ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
- self.sli = User.objects.get(username="sli")
- self.subscriber = User.objects.get(username="subscriber")
- self.public = User.objects.get(username="public")
+class ElectionTest(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.election = Election.objects.first()
+ cls.public_group = Group.objects.get(id=settings.SITH_GROUP_PUBLIC_ID)
+ cls.subscriber_group = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
+ cls.ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
+ cls.sli = User.objects.get(username="sli")
+ cls.subscriber = User.objects.get(username="subscriber")
+ cls.public = User.objects.get(username="public")
-class ElectionDetailTest(MainElection):
+class ElectionDetailTest(ElectionTest):
def test_permission_denied(self):
self.election.view_groups.remove(self.public_group)
- self.election.view_groups.add(self.subscriber_group)
- self.election.save()
- self.client.login(username=self.public.username, password="plop")
- response_get = self.client.get(
+ self.client.force_login(self.public)
+ response = self.client.get(
reverse("election:detail", args=str(self.election.id))
)
- response_post = self.client.get(
- reverse("election:detail", args=str(self.election.id))
- )
- self.assertTrue(response_get.status_code == 403)
- self.assertTrue(response_post.status_code == 403)
- self.election.view_groups.remove(self.subscriber_group)
- self.election.view_groups.add(self.public_group)
- self.election.save()
+ assert response.status_code == 403
def test_permisson_granted(self):
- self.client.login(username=self.public.username, password="plop")
- response_get = self.client.get(
+ self.client.force_login(self.public)
+ response = self.client.get(
reverse("election:detail", args=str(self.election.id))
)
- response_post = self.client.post(
- reverse("election:detail", args=str(self.election.id))
- )
- self.assertFalse(response_get.status_code == 403)
- self.assertFalse(response_post.status_code == 403)
- self.assertTrue("La roue tourne" in str(response_get.content))
+ assert response.status_code == 200
+ assert "La roue tourne" in str(response.content)
-class ElectionUpdateView(MainElection):
+class ElectionUpdateView(ElectionTest):
def test_permission_denied(self):
- self.client.login(username=self.subscriber.username, password="plop")
- response_get = self.client.get(
+ self.client.force_login(self.subscriber)
+ response = self.client.get(
reverse("election:update", args=str(self.election.id))
)
- response_post = self.client.post(
+ assert response.status_code == 403
+ response = self.client.post(
reverse("election:update", args=str(self.election.id))
)
- self.assertTrue(response_get.status_code == 403)
- self.assertTrue(response_post.status_code == 403)
+ assert response.status_code == 403
diff --git a/election/views.py b/election/views.py
index a223c78c..71be8bfe 100644
--- a/election/views.py
+++ b/election/views.py
@@ -1,24 +1,19 @@
-from django.shortcuts import get_object_or_404
-from django.views.generic import ListView, DetailView
-from django.views.generic.edit import UpdateView, CreateView
-from django.views.generic.edit import DeleteView, FormView
-from django.urls import reverse_lazy, reverse
-from django.utils.translation import gettext_lazy as _
+from ajax_select import make_ajax_field
+from ajax_select.fields import AutoCompleteSelectField
+from django import forms
from django.core.exceptions import PermissionDenied
from django.db import transaction
-from django.shortcuts import redirect
-from django import forms
+from django.db.models.query import QuerySet
+from django.shortcuts import get_object_or_404, redirect
+from django.urls import reverse, reverse_lazy
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, ListView
+from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from core.models import User
-from core.views import CanViewMixin, CanEditMixin, CanCreateMixin
-from django.db.models.query import QuerySet
-from core.views.forms import SelectDateTime, MarkdownInput
-from election.models import Election, Role, Candidature, ElectionList, Vote
-from core.views.forms import TzAwareDateTimeField
-
-from ajax_select.fields import AutoCompleteSelectField
-from ajax_select import make_ajax_field
-
+from core.views import CanCreateMixin, CanEditMixin, CanViewMixin
+from core.views.forms import MarkdownInput, TzAwareDateTimeField
+from election.models import Candidature, Election, ElectionList, Role, Vote
# Custom form field
diff --git a/forum/admin.py b/forum/admin.py
index 6d0c088a..eff7d401 100644
--- a/forum/admin.py
+++ b/forum/admin.py
@@ -17,7 +17,6 @@
from django.contrib import admin
from haystack.admin import SearchModelAdmin
-
from forum.models import *
diff --git a/forum/migrations/0001_initial.py b/forum/migrations/0001_initial.py
index 6c06b0a6..3d3440db 100644
--- a/forum/migrations/0001_initial.py
+++ b/forum/migrations/0001_initial.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.utils.timezone import utc
-from django.conf import settings
-import django.utils.timezone
import datetime
+from datetime import timezone
+
import django.db.models.deletion
+import django.utils.timezone
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -225,7 +226,9 @@ class Migration(migrations.Migration):
"last_read_date",
models.DateTimeField(
verbose_name="last read date",
- default=datetime.datetime(1999, 1, 1, 0, 0, tzinfo=utc),
+ default=datetime.datetime(
+ 1999, 1, 1, 0, 0, tzinfo=timezone.utc
+ ),
),
),
(
diff --git a/forum/migrations/0004_auto_20170531_1949.py b/forum/migrations/0004_auto_20170531_1949.py
index 152bdaea..ef466a9b 100644
--- a/forum/migrations/0004_auto_20170531_1949.py
+++ b/forum/migrations/0004_auto_20170531_1949.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/forum/migrations/0005_forumtopic_subscribed_users.py b/forum/migrations/0005_forumtopic_subscribed_users.py
index b1f6d629..b977aadf 100644
--- a/forum/migrations/0005_forumtopic_subscribed_users.py
+++ b/forum/migrations/0005_forumtopic_subscribed_users.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/forum/migrations/0006_auto_20180426_2013.py b/forum/migrations/0006_auto_20180426_2013.py
index 30248dec..974b9b8e 100644
--- a/forum/migrations/0006_auto_20180426_2013.py
+++ b/forum/migrations/0006_auto_20180426_2013.py
@@ -2,6 +2,7 @@
from __future__ import unicode_literals
from django.db import migrations, models
+
import forum.models
diff --git a/forum/models.py b/forum/models.py
index 3894caa7..a7d77c4d 100644
--- a/forum/models.py
+++ b/forum/models.py
@@ -22,20 +22,20 @@
#
#
-from django.db import models
+from datetime import datetime
+from itertools import chain
+
+import pytz
from django.conf import settings
-from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
+from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
+from django.utils.translation import gettext_lazy as _
-from datetime import datetime
-from itertools import chain
-import pytz
-
-from core.models import User, Group
from club.models import Club
+from core.models import Group, User
class Forum(models.Model):
diff --git a/forum/tests.py b/forum/tests.py
index 46a200c2..d888e761 100644
--- a/forum/tests.py
+++ b/forum/tests.py
@@ -14,6 +14,4 @@
#
#
-from django.test import TestCase
-
# Create your tests here.
diff --git a/forum/views.py b/forum/views.py
index 9b75794b..74e6bff4 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -23,31 +23,30 @@
#
#
-from django.shortcuts import get_object_or_404
-from django.views.generic import ListView, DetailView, RedirectView
-from django.views.generic.edit import UpdateView, CreateView, DeleteView
-from django.views.generic.detail import SingleObjectMixin
-from django.utils.translation import gettext_lazy as _
-from django.urls import reverse_lazy
-from django.utils import timezone, html
-from django.conf import settings
-from django import forms
-from django.core.exceptions import PermissionDenied
-from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
-
from ajax_select import make_ajax_field
+from django import forms
+from django.conf import settings
+from django.core.exceptions import PermissionDenied
+from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
+from django.shortcuts import get_object_or_404
+from django.urls import reverse_lazy
+from django.utils import html, timezone
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, ListView, RedirectView
+from django.views.generic.detail import SingleObjectMixin
+from django.views.generic.edit import CreateView, DeleteView, UpdateView
+from haystack.query import RelatedSearchQuerySet
from core.views import (
- CanViewMixin,
+ CanCreateMixin,
CanEditMixin,
CanEditPropMixin,
- CanCreateMixin,
+ CanViewMixin,
UserIsLoggedMixin,
can_view,
)
from core.views.forms import MarkdownInput
-from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
-from haystack.query import RelatedSearchQuerySet
+from forum.models import Forum, ForumMessage, ForumMessageMeta, ForumTopic
class ForumSearchView(ListView):
diff --git a/galaxy/management/commands/generate_galaxy_test_data.py b/galaxy/management/commands/generate_galaxy_test_data.py
index f6442487..15cf7f48 100644
--- a/galaxy/management/commands/generate_galaxy_test_data.py
+++ b/galaxy/management/commands/generate_galaxy_test_data.py
@@ -21,7 +21,9 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
+import logging
import warnings
+from datetime import timedelta
from typing import Final, Optional
from django.conf import settings
@@ -29,15 +31,10 @@ from django.core.files.base import ContentFile
from django.core.management.base import BaseCommand
from django.utils import timezone
-from datetime import timedelta
-
-import logging
-
from club.models import Club, Membership
-from core.models import User, Group, Page, SithFile
+from core.models import Group, Page, SithFile, User
+from sas.models import Album, PeoplePictureRelation, Picture
from subscription.models import Subscription
-from sas.models import Album, Picture, PeoplePictureRelation
-
RED_PIXEL_PNG: Final[bytes] = (
b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
@@ -131,7 +128,7 @@ class Command(BaseCommand):
self.clubs.append(Club(unix_name=f"galaxy-club-{i}", name=f"club-{i}"))
# We don't need to create corresponding groups here, as the Galaxy doesn't care about them
Club.objects.bulk_create(self.clubs)
- self.clubs = Club.objects.filter(unix_name__startswith="galaxy-").all()
+ self.clubs = list(Club.objects.filter(unix_name__startswith="galaxy-").all())
def make_users(self):
"""
@@ -150,20 +147,20 @@ class Command(BaseCommand):
self.logger.info(f"Creating {u}")
self.users.append(u)
User.objects.bulk_create(self.users)
- self.users = User.objects.filter(username__startswith="galaxy-").all()
+ self.users = list(User.objects.filter(username__startswith="galaxy-").all())
# now that users are created, create their subscription
subs = []
- for i in range(self.NB_USERS):
- u = self.users[i]
- self.logger.info(f"Registering {u}")
+ end = Subscription.compute_end(duration=2)
+ for i, user in enumerate(self.users):
+ self.logger.info(f"Registering {user}")
subs.append(
Subscription(
- member=u,
+ member=user,
subscription_start=Subscription.compute_start(
self.now - timedelta(days=self.NB_USERS - i)
),
- subscription_end=Subscription.compute_end(duration=2),
+ subscription_end=end,
)
)
Subscription.objects.bulk_create(subs)
@@ -178,20 +175,22 @@ class Command(BaseCommand):
Then it will take 14 other citizen among the previous 200
(godfathers are usually older), and apply another
heuristic to determine whether they should have a family link
+ It will result in approximately 40% of users having at least one godchild
+ and 70% having at least one godfather.
"""
if self.users is None:
raise RuntimeError(
"The `make_users()` method must be called before `make_families()`"
)
+ godfathers = []
for i in range(200, self.NB_USERS):
- godfathers = []
for j in range(i - 200, i, 14): # this will loop 14 times (14² = 196)
- if (i / 10) % 10 == (i + j) % 10:
+ if (i // 10) % 10 == (i + j) % 10:
u1 = self.users[i]
u2 = self.users[j]
+ godfathers.append(User.godfathers.through(from_user=u1, to_user=u2))
self.logger.info(f"Making {u2} the godfather of {u1}")
- godfathers.append(u2)
- u1.godfathers.set(godfathers)
+ User.godfathers.through.objects.bulk_create(godfathers)
def make_club_memberships(self):
"""
@@ -298,7 +297,7 @@ class Command(BaseCommand):
self.picts[i].compressed.name = self.picts[i].name
self.picts[i].thumbnail.name = self.picts[i].name
Picture.objects.bulk_create(self.picts)
- self.picts = Picture.objects.filter(name__startswith="galaxy-").all()
+ self.picts = list(Picture.objects.filter(name__startswith="galaxy-").all())
def make_pictures_memberships(self):
"""
@@ -380,32 +379,24 @@ class Command(BaseCommand):
u1 = self.users[uid]
u2 = self.users[uid - 100]
u3 = self.users[uid + 100]
- u1.godfathers.add(u2)
- u1.godchildren.add(u3)
+ User.godfathers.through.objects.bulk_create(
+ [
+ User.godfathers.through(from_user=u1, to_user=u2),
+ User.godfathers.through(from_user=u3, to_user=u2),
+ ],
+ ignore_conflicts=True, # in case a relationship has already been created
+ )
self.logger.info(f"{u1} will be important and close to {u2} and {u3}")
pictures_tags = []
- for p in range( # Mix them with other citizen for more chaos
- uid - 400, uid - 200
- ):
- # users may already be on the pictures
- if not self.picts[p].people.filter(user=u1).exists():
- pictures_tags.append(
- PeoplePictureRelation(user=u1, picture=self.picts[p])
- )
- if not self.picts[p].people.filter(user=u2).exists():
- pictures_tags.append(
- PeoplePictureRelation(user=u2, picture=self.picts[p])
- )
- if not self.picts[p + self.NB_USERS].people.filter(user=u1).exists():
- pictures_tags.append(
- PeoplePictureRelation(
- user=u1, picture=self.picts[p + self.NB_USERS]
- )
- )
- if not self.picts[p + self.NB_USERS].people.filter(user=u2).exists():
- pictures_tags.append(
- PeoplePictureRelation(
- user=u2, picture=self.picts[p + self.NB_USERS]
- )
- )
- PeoplePictureRelation.objects.bulk_create(pictures_tags)
+ for p in range(uid - 400, uid - 200):
+ # Mix them with other citizen for more chaos
+ pictures_tags += [
+ PeoplePictureRelation(user=u1, picture=self.picts[p]),
+ PeoplePictureRelation(user=u2, picture=self.picts[p]),
+ PeoplePictureRelation(user=u1, picture=self.picts[p + self.NB_USERS]),
+ PeoplePictureRelation(user=u2, picture=self.picts[p + self.NB_USERS]),
+ ]
+ # users may already be on the pictures.
+ # In this case the conflict will just be ignored
+ # and nothing will happen for this entry
+ PeoplePictureRelation.objects.bulk_create(pictures_tags, ignore_conflicts=True)
diff --git a/galaxy/management/commands/rule_galaxy.py b/galaxy/management/commands/rule_galaxy.py
index 55cb9ae9..1743fadc 100644
--- a/galaxy/management/commands/rule_galaxy.py
+++ b/galaxy/management/commands/rule_galaxy.py
@@ -21,6 +21,7 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
+import logging
import warnings
from django.core.management.base import BaseCommand
@@ -28,8 +29,6 @@ from django.db import connection
from galaxy.models import Galaxy
-import logging
-
class Command(BaseCommand):
help = (
diff --git a/galaxy/migrations/0001_initial.py b/galaxy/migrations/0001_initial.py
index ab9a6a06..7c8e8521 100644
--- a/galaxy/migrations/0001_initial.py
+++ b/galaxy/migrations/0001_initial.py
@@ -1,8 +1,8 @@
# Generated by Django 3.2.16 on 2023-03-02 10:07
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
diff --git a/galaxy/migrations/0002_auto_20230412_1130.py b/galaxy/migrations/0002_auto_20230412_1130.py
index be01af99..00a69d00 100644
--- a/galaxy/migrations/0002_auto_20230412_1130.py
+++ b/galaxy/migrations/0002_auto_20230412_1130.py
@@ -1,8 +1,8 @@
# Generated by Django 3.2.16 on 2023-04-12 09:30
+import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
-import django.db.models.deletion
class Migration(migrations.Migration):
diff --git a/galaxy/models.py b/galaxy/models.py
index 744cca79..cca7397d 100644
--- a/galaxy/models.py
+++ b/galaxy/models.py
@@ -24,20 +24,19 @@
from __future__ import annotations
-import math
import logging
+import math
import time
-
-from typing import List, TypedDict, NamedTuple, Union, Optional
+from typing import List, NamedTuple, Optional, TypedDict, Union
from django.db import models
-from django.db.models import Q, Case, F, Value, When, Count
+from django.db.models import Case, Count, F, Q, Value, When
from django.db.models.functions import Concat
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
-from core.models import User
from club.models import Club
+from core.models import User
from sas.models import Picture
diff --git a/galaxy/ref_galaxy_state.json b/galaxy/ref_galaxy_state.json
index 7c19b796..6f08a6a6 100644
--- a/galaxy/ref_galaxy_state.json
+++ b/galaxy/ref_galaxy_state.json
@@ -1 +1 @@
-{"nodes": [{"id": 215, "name": "Citizen n\u00b0202", "mass": 5}, {"id": 219, "name": "Citizen n\u00b0206", "mass": 5}, {"id": 221, "name": "Citizen n\u00b0208", "mass": 5}, {"id": 225, "name": "Citizen n\u00b0212", "mass": 5}, {"id": 227, "name": "Citizen n\u00b0214", "mass": 5}, {"id": 228, "name": "Citizen n\u00b0215", "mass": 5}, {"id": 231, "name": "Citizen n\u00b0218", "mass": 5}, {"id": 233, "name": "Citizen n\u00b0220", "mass": 5}, {"id": 243, "name": "Citizen n\u00b0230", "mass": 5}, {"id": 245, "name": "Citizen n\u00b0232", "mass": 5}, {"id": 248, "name": "Citizen n\u00b0235", "mass": 5}, {"id": 249, "name": "Citizen n\u00b0236", "mass": 5}, {"id": 251, "name": "Citizen n\u00b0238", "mass": 5}, {"id": 255, "name": "Citizen n\u00b0242", "mass": 5}, {"id": 257, "name": "Citizen n\u00b0244", "mass": 5}, {"id": 261, "name": "Citizen n\u00b0248", "mass": 5}, {"id": 263, "name": "Citizen n\u00b0250", "mass": 5}, {"id": 273, "name": "Citizen n\u00b0260", "mass": 6}, {"id": 275, "name": "Citizen n\u00b0262", "mass": 5}, {"id": 278, "name": "Citizen n\u00b0265", "mass": 5}, {"id": 279, "name": "Citizen n\u00b0266", "mass": 5}, {"id": 281, "name": "Citizen n\u00b0268", "mass": 5}, {"id": 285, "name": "Citizen n\u00b0272", "mass": 5}, {"id": 287, "name": "Citizen n\u00b0274", "mass": 5}, {"id": 291, "name": "Citizen n\u00b0278", "mass": 5}, {"id": 293, "name": "Citizen n\u00b0280", "mass": 6}, {"id": 303, "name": "Citizen n\u00b0290", "mass": 6}, {"id": 305, "name": "Citizen n\u00b0292", "mass": 5}, {"id": 309, "name": "Citizen n\u00b0296", "mass": 5}, {"id": 311, "name": "Citizen n\u00b0298", "mass": 5}, {"id": 315, "name": "Citizen n\u00b0302", "mass": 5}, {"id": 317, "name": "Citizen n\u00b0304", "mass": 5}, {"id": 321, "name": "Citizen n\u00b0308", "mass": 5}, {"id": 323, "name": "Citizen n\u00b0310", "mass": 6}, {"id": 333, "name": "Citizen n\u00b0320", "mass": 5}, {"id": 335, "name": "Citizen n\u00b0322", "mass": 5}, {"id": 339, "name": "Citizen n\u00b0326", "mass": 5}, {"id": 341, "name": "Citizen n\u00b0328", "mass": 5}, {"id": 345, "name": "Citizen n\u00b0332", "mass": 5}, {"id": 347, "name": "Citizen n\u00b0334", "mass": 5}, {"id": 348, "name": "Citizen n\u00b0335", "mass": 5}, {"id": 351, "name": "Citizen n\u00b0338", "mass": 5}, {"id": 353, "name": "Citizen n\u00b0340", "mass": 6}, {"id": 363, "name": "Citizen n\u00b0350", "mass": 5}, {"id": 365, "name": "Citizen n\u00b0352", "mass": 5}, {"id": 369, "name": "Citizen n\u00b0356", "mass": 5}, {"id": 371, "name": "Citizen n\u00b0358", "mass": 5}, {"id": 375, "name": "Citizen n\u00b0362", "mass": 5}, {"id": 377, "name": "Citizen n\u00b0364", "mass": 5}, {"id": 378, "name": "Citizen n\u00b0365", "mass": 5}, {"id": 381, "name": "Citizen n\u00b0368", "mass": 5}, {"id": 383, "name": "Citizen n\u00b0370", "mass": 5}, {"id": 393, "name": "Citizen n\u00b0380", "mass": 5}, {"id": 395, "name": "Citizen n\u00b0382", "mass": 5}, {"id": 398, "name": "Citizen n\u00b0385", "mass": 5}, {"id": 399, "name": "Citizen n\u00b0386", "mass": 5}, {"id": 401, "name": "Citizen n\u00b0388", "mass": 5}, {"id": 405, "name": "Citizen n\u00b0392", "mass": 5}, {"id": 407, "name": "Citizen n\u00b0394", "mass": 5}, {"id": 411, "name": "Citizen n\u00b0398", "mass": 5}, {"id": 413, "name": "Citizen n\u00b0400", "mass": 10}, {"id": 423, "name": "Citizen n\u00b0410", "mass": 6}, {"id": 425, "name": "Citizen n\u00b0412", "mass": 5}, {"id": 428, "name": "Citizen n\u00b0415", "mass": 5}, {"id": 429, "name": "Citizen n\u00b0416", "mass": 5}, {"id": 431, "name": "Citizen n\u00b0418", "mass": 5}, {"id": 435, "name": "Citizen n\u00b0422", "mass": 5}, {"id": 437, "name": "Citizen n\u00b0424", "mass": 5}, {"id": 441, "name": "Citizen n\u00b0428", "mass": 5}, {"id": 443, "name": "Citizen n\u00b0430", "mass": 5}, {"id": 453, "name": "Citizen n\u00b0440", "mass": 6}, {"id": 455, "name": "Citizen n\u00b0442", "mass": 5}, {"id": 459, "name": "Citizen n\u00b0446", "mass": 5}, {"id": 461, "name": "Citizen n\u00b0448", "mass": 5}, {"id": 465, "name": "Citizen n\u00b0452", "mass": 5}, {"id": 467, "name": "Citizen n\u00b0454", "mass": 5}, {"id": 471, "name": "Citizen n\u00b0458", "mass": 5}, {"id": 473, "name": "Citizen n\u00b0460", "mass": 6}, {"id": 483, "name": "Citizen n\u00b0470", "mass": 5}, {"id": 485, "name": "Citizen n\u00b0472", "mass": 5}, {"id": 489, "name": "Citizen n\u00b0476", "mass": 5}, {"id": 491, "name": "Citizen n\u00b0478", "mass": 5}, {"id": 495, "name": "Citizen n\u00b0482", "mass": 5}, {"id": 497, "name": "Citizen n\u00b0484", "mass": 5}, {"id": 498, "name": "Citizen n\u00b0485", "mass": 5}, {"id": 501, "name": "Citizen n\u00b0488", "mass": 5}, {"id": 503, "name": "Citizen n\u00b0490", "mass": 6}, {"id": 513, "name": "Citizen n\u00b0500", "mass": 10}, {"id": 515, "name": "Citizen n\u00b0502", "mass": 5}, {"id": 519, "name": "Citizen n\u00b0506", "mass": 5}, {"id": 521, "name": "Citizen n\u00b0508", "mass": 5}, {"id": 525, "name": "Citizen n\u00b0512", "mass": 5}, {"id": 527, "name": "Citizen n\u00b0514", "mass": 5}, {"id": 528, "name": "Citizen n\u00b0515", "mass": 5}, {"id": 531, "name": "Citizen n\u00b0518", "mass": 5}, {"id": 533, "name": "Citizen n\u00b0520", "mass": 5}, {"id": 543, "name": "Citizen n\u00b0530", "mass": 5}, {"id": 545, "name": "Citizen n\u00b0532", "mass": 5}, {"id": 548, "name": "Citizen n\u00b0535", "mass": 5}, {"id": 549, "name": "Citizen n\u00b0536", "mass": 5}, {"id": 551, "name": "Citizen n\u00b0538", "mass": 5}, {"id": 555, "name": "Citizen n\u00b0542", "mass": 5}, {"id": 557, "name": "Citizen n\u00b0544", "mass": 5}, {"id": 561, "name": "Citizen n\u00b0548", "mass": 5}, {"id": 563, "name": "Citizen n\u00b0550", "mass": 5}, {"id": 573, "name": "Citizen n\u00b0560", "mass": 6}, {"id": 575, "name": "Citizen n\u00b0562", "mass": 5}, {"id": 578, "name": "Citizen n\u00b0565", "mass": 5}, {"id": 579, "name": "Citizen n\u00b0566", "mass": 5}, {"id": 581, "name": "Citizen n\u00b0568", "mass": 5}, {"id": 585, "name": "Citizen n\u00b0572", "mass": 5}, {"id": 587, "name": "Citizen n\u00b0574", "mass": 5}, {"id": 591, "name": "Citizen n\u00b0578", "mass": 5}, {"id": 593, "name": "Citizen n\u00b0580", "mass": 5}, {"id": 603, "name": "Citizen n\u00b0590", "mass": 6}, {"id": 605, "name": "Citizen n\u00b0592", "mass": 5}, {"id": 609, "name": "Citizen n\u00b0596", "mass": 5}, {"id": 611, "name": "Citizen n\u00b0598", "mass": 5}, {"id": 615, "name": "Citizen n\u00b0602", "mass": 5}, {"id": 617, "name": "Citizen n\u00b0604", "mass": 5}, {"id": 621, "name": "Citizen n\u00b0608", "mass": 5}, {"id": 623, "name": "Citizen n\u00b0610", "mass": 6}, {"id": 633, "name": "Citizen n\u00b0620", "mass": 5}, {"id": 635, "name": "Citizen n\u00b0622", "mass": 5}, {"id": 639, "name": "Citizen n\u00b0626", "mass": 5}, {"id": 641, "name": "Citizen n\u00b0628", "mass": 5}, {"id": 645, "name": "Citizen n\u00b0632", "mass": 5}, {"id": 647, "name": "Citizen n\u00b0634", "mass": 5}, {"id": 648, "name": "Citizen n\u00b0635", "mass": 5}, {"id": 651, "name": "Citizen n\u00b0638", "mass": 5}, {"id": 653, "name": "Citizen n\u00b0640", "mass": 6}, {"id": 663, "name": "Citizen n\u00b0650", "mass": 5}, {"id": 665, "name": "Citizen n\u00b0652", "mass": 5}, {"id": 669, "name": "Citizen n\u00b0656", "mass": 5}, {"id": 671, "name": "Citizen n\u00b0658", "mass": 5}, {"id": 675, "name": "Citizen n\u00b0662", "mass": 5}, {"id": 677, "name": "Citizen n\u00b0664", "mass": 5}, {"id": 678, "name": "Citizen n\u00b0665", "mass": 5}, {"id": 681, "name": "Citizen n\u00b0668", "mass": 5}, {"id": 683, "name": "Citizen n\u00b0670", "mass": 5}, {"id": 693, "name": "Citizen n\u00b0680", "mass": 5}, {"id": 695, "name": "Citizen n\u00b0682", "mass": 5}, {"id": 698, "name": "Citizen n\u00b0685", "mass": 5}, {"id": 699, "name": "Citizen n\u00b0686", "mass": 5}, {"id": 701, "name": "Citizen n\u00b0688", "mass": 5}, {"id": 705, "name": "Citizen n\u00b0692", "mass": 5}, {"id": 707, "name": "Citizen n\u00b0694", "mass": 5}, {"id": 711, "name": "Citizen n\u00b0698", "mass": 5}, {"id": 713, "name": "Citizen n\u00b0700", "mass": 6}, {"id": 723, "name": "Citizen n\u00b0710", "mass": 6}, {"id": 725, "name": "Citizen n\u00b0712", "mass": 5}, {"id": 728, "name": "Citizen n\u00b0715", "mass": 5}, {"id": 729, "name": "Citizen n\u00b0716", "mass": 5}, {"id": 731, "name": "Citizen n\u00b0718", "mass": 5}, {"id": 735, "name": "Citizen n\u00b0722", "mass": 5}, {"id": 737, "name": "Citizen n\u00b0724", "mass": 5}, {"id": 741, "name": "Citizen n\u00b0728", "mass": 5}, {"id": 743, "name": "Citizen n\u00b0730", "mass": 5}, {"id": 753, "name": "Citizen n\u00b0740", "mass": 6}, {"id": 755, "name": "Citizen n\u00b0742", "mass": 5}, {"id": 759, "name": "Citizen n\u00b0746", "mass": 5}, {"id": 761, "name": "Citizen n\u00b0748", "mass": 5}, {"id": 765, "name": "Citizen n\u00b0752", "mass": 5}, {"id": 767, "name": "Citizen n\u00b0754", "mass": 5}, {"id": 771, "name": "Citizen n\u00b0758", "mass": 5}, {"id": 773, "name": "Citizen n\u00b0760", "mass": 6}, {"id": 783, "name": "Citizen n\u00b0770", "mass": 5}, {"id": 785, "name": "Citizen n\u00b0772", "mass": 5}, {"id": 789, "name": "Citizen n\u00b0776", "mass": 5}, {"id": 791, "name": "Citizen n\u00b0778", "mass": 5}, {"id": 795, "name": "Citizen n\u00b0782", "mass": 5}, {"id": 797, "name": "Citizen n\u00b0784", "mass": 5}, {"id": 798, "name": "Citizen n\u00b0785", "mass": 5}, {"id": 801, "name": "Citizen n\u00b0788", "mass": 5}, {"id": 803, "name": "Citizen n\u00b0790", "mass": 5}], "links": [{"source": 228, "target": 225, "value": 8}, {"source": 231, "target": 221, "value": 9}, {"source": 233, "target": 221, "value": 4}, {"source": 245, "target": 233, "value": 4}, {"source": 248, "target": 219, "value": 9}, {"source": 248, "target": 233, "value": 4}, {"source": 248, "target": 245, "value": 8}, {"source": 249, "target": 228, "value": 8}, {"source": 251, "target": 245, "value": 10}, {"source": 251, "target": 249, "value": 9}, {"source": 255, "target": 245, "value": 9}, {"source": 255, "target": 251, "value": 7}, {"source": 257, "target": 219, "value": 8}, {"source": 257, "target": 233, "value": 4}, {"source": 257, "target": 245, "value": 8}, {"source": 257, "target": 248, "value": 7}, {"source": 261, "target": 221, "value": 10}, {"source": 261, "target": 249, "value": 8}, {"source": 273, "target": 219, "value": 10}, {"source": 273, "target": 221, "value": 2}, {"source": 273, "target": 231, "value": 9}, {"source": 273, "target": 248, "value": 9}, {"source": 273, "target": 257, "value": 9}, {"source": 273, "target": 261, "value": 8}, {"source": 275, "target": 225, "value": 3}, {"source": 275, "target": 228, "value": 8}, {"source": 278, "target": 225, "value": 7}, {"source": 278, "target": 228, "value": 3}, {"source": 278, "target": 249, "value": 9}, {"source": 278, "target": 275, "value": 8}, {"source": 279, "target": 221, "value": 4}, {"source": 279, "target": 227, "value": 9}, {"source": 279, "target": 233, "value": 4}, {"source": 281, "target": 221, "value": 10}, {"source": 281, "target": 231, "value": 3}, {"source": 281, "target": 243, "value": 8}, {"source": 281, "target": 273, "value": 10}, {"source": 285, "target": 233, "value": 4}, {"source": 285, "target": 245, "value": 10}, {"source": 285, "target": 248, "value": 10}, {"source": 285, "target": 261, "value": 8}, {"source": 285, "target": 273, "value": 8}, {"source": 287, "target": 225, "value": 8}, {"source": 287, "target": 263, "value": 9}, {"source": 293, "target": 221, "value": 11}, {"source": 293, "target": 243, "value": 1}, {"source": 293, "target": 245, "value": 9}, {"source": 293, "target": 251, "value": 8}, {"source": 293, "target": 255, "value": 8}, {"source": 293, "target": 281, "value": 8}, {"source": 293, "target": 285, "value": 8}, {"source": 293, "target": 291, "value": 8}, {"source": 303, "target": 227, "value": 9}, {"source": 303, "target": 228, "value": 8}, {"source": 303, "target": 249, "value": 10}, {"source": 303, "target": 278, "value": 9}, {"source": 303, "target": 279, "value": 8}, {"source": 303, "target": 293, "value": 9}, {"source": 305, "target": 245, "value": 9}, {"source": 305, "target": 251, "value": 7}, {"source": 305, "target": 255, "value": 2}, {"source": 305, "target": 293, "value": 8}, {"source": 309, "target": 219, "value": 9}, {"source": 309, "target": 221, "value": 8}, {"source": 309, "target": 248, "value": 8}, {"source": 309, "target": 257, "value": 8}, {"source": 309, "target": 261, "value": 9}, {"source": 309, "target": 263, "value": 11}, {"source": 309, "target": 273, "value": 8}, {"source": 311, "target": 249, "value": 8}, {"source": 311, "target": 261, "value": 3}, {"source": 311, "target": 285, "value": 8}, {"source": 315, "target": 215, "value": 4}, {"source": 317, "target": 221, "value": 10}, {"source": 317, "target": 227, "value": 9}, {"source": 317, "target": 233, "value": 10}, {"source": 317, "target": 243, "value": 4}, {"source": 317, "target": 255, "value": 8}, {"source": 317, "target": 279, "value": 8}, {"source": 317, "target": 293, "value": 3}, {"source": 317, "target": 305, "value": 8}, {"source": 321, "target": 219, "value": 10}, {"source": 321, "target": 221, "value": 2}, {"source": 321, "target": 225, "value": 7}, {"source": 321, "target": 228, "value": 8}, {"source": 321, "target": 233, "value": 4}, {"source": 321, "target": 249, "value": 11}, {"source": 321, "target": 251, "value": 8}, {"source": 321, "target": 261, "value": 9}, {"source": 321, "target": 275, "value": 7}, {"source": 321, "target": 278, "value": 7}, {"source": 321, "target": 279, "value": 4}, {"source": 321, "target": 293, "value": 11}, {"source": 321, "target": 309, "value": 8}, {"source": 323, "target": 219, "value": 10}, {"source": 323, "target": 221, "value": 8}, {"source": 323, "target": 248, "value": 9}, {"source": 323, "target": 257, "value": 9}, {"source": 323, "target": 261, "value": 8}, {"source": 323, "target": 273, "value": 1}, {"source": 323, "target": 309, "value": 8}, {"source": 333, "target": 221, "value": 4}, {"source": 333, "target": 233, "value": 0}, {"source": 333, "target": 243, "value": 9}, {"source": 333, "target": 245, "value": 8}, {"source": 333, "target": 248, "value": 8}, {"source": 333, "target": 257, "value": 4}, {"source": 333, "target": 279, "value": 4}, {"source": 333, "target": 281, "value": 8}, {"source": 333, "target": 285, "value": 9}, {"source": 333, "target": 293, "value": 9}, {"source": 333, "target": 309, "value": 8}, {"source": 333, "target": 317, "value": 9}, {"source": 333, "target": 321, "value": 4}, {"source": 335, "target": 233, "value": 10}, {"source": 335, "target": 243, "value": 11}, {"source": 335, "target": 273, "value": 8}, {"source": 335, "target": 285, "value": 3}, {"source": 335, "target": 293, "value": 10}, {"source": 335, "target": 333, "value": 8}, {"source": 339, "target": 243, "value": 11}, {"source": 339, "target": 293, "value": 10}, {"source": 339, "target": 335, "value": 8}, {"source": 341, "target": 251, "value": 9}, {"source": 341, "target": 291, "value": 3}, {"source": 341, "target": 293, "value": 4}, {"source": 341, "target": 303, "value": 8}, {"source": 345, "target": 233, "value": 4}, {"source": 345, "target": 245, "value": 2}, {"source": 345, "target": 248, "value": 8}, {"source": 345, "target": 257, "value": 8}, {"source": 345, "target": 285, "value": 9}, {"source": 345, "target": 333, "value": 8}, {"source": 347, "target": 221, "value": 8}, {"source": 347, "target": 273, "value": 4}, {"source": 347, "target": 285, "value": 8}, {"source": 347, "target": 293, "value": 7}, {"source": 347, "target": 323, "value": 9}, {"source": 348, "target": 219, "value": 8}, {"source": 348, "target": 233, "value": 4}, {"source": 348, "target": 245, "value": 8}, {"source": 348, "target": 248, "value": 4}, {"source": 348, "target": 257, "value": 7}, {"source": 348, "target": 273, "value": 9}, {"source": 348, "target": 285, "value": 10}, {"source": 348, "target": 309, "value": 8}, {"source": 348, "target": 323, "value": 9}, {"source": 348, "target": 333, "value": 8}, {"source": 348, "target": 345, "value": 8}, {"source": 351, "target": 251, "value": 3}, {"source": 351, "target": 263, "value": 8}, {"source": 351, "target": 309, "value": 9}, {"source": 351, "target": 341, "value": 9}, {"source": 353, "target": 215, "value": 8}, {"source": 353, "target": 227, "value": 4}, {"source": 353, "target": 228, "value": 9}, {"source": 353, "target": 249, "value": 10}, {"source": 353, "target": 278, "value": 9}, {"source": 353, "target": 279, "value": 4}, {"source": 353, "target": 293, "value": 9}, {"source": 353, "target": 303, "value": 1}, {"source": 353, "target": 315, "value": 8}, {"source": 353, "target": 341, "value": 8}, {"source": 363, "target": 255, "value": 9}, {"source": 363, "target": 261, "value": 9}, {"source": 363, "target": 263, "value": 2}, {"source": 363, "target": 285, "value": 8}, {"source": 363, "target": 287, "value": 9}, {"source": 363, "target": 305, "value": 8}, {"source": 363, "target": 309, "value": 10}, {"source": 363, "target": 311, "value": 9}, {"source": 363, "target": 317, "value": 7}, {"source": 363, "target": 351, "value": 8}, {"source": 365, "target": 215, "value": 4}, {"source": 365, "target": 225, "value": 9}, {"source": 365, "target": 227, "value": 4}, {"source": 365, "target": 228, "value": 10}, {"source": 365, "target": 275, "value": 9}, {"source": 365, "target": 278, "value": 10}, {"source": 365, "target": 291, "value": 8}, {"source": 365, "target": 293, "value": 9}, {"source": 365, "target": 315, "value": 3}, {"source": 365, "target": 317, "value": 4}, {"source": 365, "target": 321, "value": 9}, {"source": 365, "target": 341, "value": 8}, {"source": 365, "target": 353, "value": 8}, {"source": 369, "target": 219, "value": 4}, {"source": 369, "target": 221, "value": 9}, {"source": 369, "target": 231, "value": 8}, {"source": 369, "target": 248, "value": 8}, {"source": 369, "target": 257, "value": 8}, {"source": 369, "target": 273, "value": 5}, {"source": 369, "target": 281, "value": 8}, {"source": 369, "target": 309, "value": 9}, {"source": 369, "target": 321, "value": 10}, {"source": 369, "target": 323, "value": 10}, {"source": 369, "target": 348, "value": 8}, {"source": 371, "target": 221, "value": 4}, {"source": 371, "target": 261, "value": 10}, {"source": 371, "target": 293, "value": 11}, {"source": 371, "target": 309, "value": 8}, {"source": 371, "target": 321, "value": 3}, {"source": 375, "target": 225, "value": 4}, {"source": 375, "target": 228, "value": 8}, {"source": 375, "target": 275, "value": 4}, {"source": 375, "target": 278, "value": 8}, {"source": 375, "target": 321, "value": 7}, {"source": 375, "target": 365, "value": 9}, {"source": 377, "target": 221, "value": 10}, {"source": 377, "target": 225, "value": 9}, {"source": 377, "target": 227, "value": 4}, {"source": 377, "target": 231, "value": 8}, {"source": 377, "target": 243, "value": 10}, {"source": 377, "target": 255, "value": 10}, {"source": 377, "target": 273, "value": 4}, {"source": 377, "target": 275, "value": 9}, {"source": 377, "target": 279, "value": 10}, {"source": 377, "target": 281, "value": 8}, {"source": 377, "target": 285, "value": 9}, {"source": 377, "target": 293, "value": 10}, {"source": 377, "target": 303, "value": 9}, {"source": 377, "target": 305, "value": 10}, {"source": 377, "target": 317, "value": 9}, {"source": 377, "target": 335, "value": 4}, {"source": 377, "target": 339, "value": 8}, {"source": 377, "target": 353, "value": 4}, {"source": 377, "target": 365, "value": 4}, {"source": 377, "target": 369, "value": 7}, {"source": 377, "target": 375, "value": 8}, {"source": 378, "target": 225, "value": 8}, {"source": 378, "target": 228, "value": 4}, {"source": 378, "target": 249, "value": 8}, {"source": 378, "target": 275, "value": 8}, {"source": 378, "target": 278, "value": 4}, {"source": 378, "target": 303, "value": 9}, {"source": 378, "target": 321, "value": 8}, {"source": 378, "target": 353, "value": 9}, {"source": 378, "target": 365, "value": 10}, {"source": 378, "target": 375, "value": 8}, {"source": 381, "target": 221, "value": 10}, {"source": 381, "target": 231, "value": 4}, {"source": 381, "target": 233, "value": 4}, {"source": 381, "target": 243, "value": 8}, {"source": 381, "target": 257, "value": 8}, {"source": 381, "target": 273, "value": 10}, {"source": 381, "target": 281, "value": 2}, {"source": 381, "target": 291, "value": 9}, {"source": 381, "target": 293, "value": 8}, {"source": 381, "target": 333, "value": 2}, {"source": 381, "target": 341, "value": 9}, {"source": 381, "target": 369, "value": 8}, {"source": 381, "target": 377, "value": 8}, {"source": 383, "target": 221, "value": 4}, {"source": 383, "target": 233, "value": 1}, {"source": 383, "target": 257, "value": 8}, {"source": 383, "target": 279, "value": 4}, {"source": 383, "target": 317, "value": 9}, {"source": 383, "target": 321, "value": 4}, {"source": 383, "target": 333, "value": 1}, {"source": 383, "target": 335, "value": 9}, {"source": 383, "target": 381, "value": 7}, {"source": 393, "target": 243, "value": 1}, {"source": 393, "target": 245, "value": 9}, {"source": 393, "target": 251, "value": 8}, {"source": 393, "target": 255, "value": 8}, {"source": 393, "target": 281, "value": 8}, {"source": 393, "target": 293, "value": 1}, {"source": 393, "target": 305, "value": 8}, {"source": 393, "target": 317, "value": 3}, {"source": 393, "target": 333, "value": 9}, {"source": 393, "target": 335, "value": 10}, {"source": 393, "target": 339, "value": 10}, {"source": 393, "target": 377, "value": 9}, {"source": 393, "target": 381, "value": 8}, {"source": 395, "target": 233, "value": 4}, {"source": 395, "target": 245, "value": 4}, {"source": 395, "target": 248, "value": 8}, {"source": 395, "target": 285, "value": 10}, {"source": 395, "target": 333, "value": 8}, {"source": 395, "target": 345, "value": 3}, {"source": 395, "target": 348, "value": 8}, {"source": 398, "target": 219, "value": 8}, {"source": 398, "target": 233, "value": 4}, {"source": 398, "target": 243, "value": 9}, {"source": 398, "target": 245, "value": 8}, {"source": 398, "target": 248, "value": 4}, {"source": 398, "target": 257, "value": 8}, {"source": 398, "target": 273, "value": 10}, {"source": 398, "target": 281, "value": 8}, {"source": 398, "target": 285, "value": 10}, {"source": 398, "target": 293, "value": 9}, {"source": 398, "target": 309, "value": 9}, {"source": 398, "target": 323, "value": 9}, {"source": 398, "target": 333, "value": 4}, {"source": 398, "target": 345, "value": 7}, {"source": 398, "target": 348, "value": 3}, {"source": 398, "target": 369, "value": 8}, {"source": 398, "target": 381, "value": 8}, {"source": 398, "target": 393, "value": 9}, {"source": 398, "target": 395, "value": 8}, {"source": 399, "target": 228, "value": 9}, {"source": 399, "target": 249, "value": 4}, {"source": 399, "target": 251, "value": 10}, {"source": 399, "target": 278, "value": 8}, {"source": 399, "target": 303, "value": 11}, {"source": 399, "target": 321, "value": 11}, {"source": 399, "target": 353, "value": 10}, {"source": 399, "target": 378, "value": 8}, {"source": 401, "target": 251, "value": 4}, {"source": 401, "target": 263, "value": 8}, {"source": 401, "target": 341, "value": 9}, {"source": 401, "target": 351, "value": 3}, {"source": 401, "target": 363, "value": 8}, {"source": 405, "target": 215, "value": 9}, {"source": 405, "target": 233, "value": 10}, {"source": 405, "target": 245, "value": 10}, {"source": 405, "target": 251, "value": 8}, {"source": 405, "target": 255, "value": 2}, {"source": 405, "target": 263, "value": 11}, {"source": 405, "target": 293, "value": 8}, {"source": 405, "target": 303, "value": 9}, {"source": 405, "target": 305, "value": 2}, {"source": 405, "target": 309, "value": 8}, {"source": 405, "target": 315, "value": 9}, {"source": 405, "target": 317, "value": 8}, {"source": 405, "target": 333, "value": 10}, {"source": 405, "target": 335, "value": 8}, {"source": 405, "target": 351, "value": 9}, {"source": 405, "target": 353, "value": 4}, {"source": 405, "target": 363, "value": 4}, {"source": 405, "target": 365, "value": 9}, {"source": 405, "target": 377, "value": 11}, {"source": 405, "target": 383, "value": 10}, {"source": 405, "target": 393, "value": 8}, {"source": 407, "target": 233, "value": 4}, {"source": 407, "target": 245, "value": 8}, {"source": 407, "target": 257, "value": 4}, {"source": 407, "target": 333, "value": 4}, {"source": 407, "target": 345, "value": 7}, {"source": 407, "target": 381, "value": 8}, {"source": 407, "target": 383, "value": 9}, {"source": 411, "target": 249, "value": 8}, {"source": 411, "target": 261, "value": 4}, {"source": 411, "target": 285, "value": 8}, {"source": 411, "target": 311, "value": 4}, {"source": 411, "target": 363, "value": 9}, {"source": 413, "target": 215, "value": 26}, {"source": 413, "target": 219, "value": 27}, {"source": 413, "target": 221, "value": 26}, {"source": 413, "target": 225, "value": 6}, {"source": 413, "target": 227, "value": 28}, {"source": 413, "target": 228, "value": 6}, {"source": 413, "target": 231, "value": 27}, {"source": 413, "target": 233, "value": 27}, {"source": 413, "target": 243, "value": 29}, {"source": 413, "target": 245, "value": 28}, {"source": 413, "target": 248, "value": 28}, {"source": 413, "target": 249, "value": 29}, {"source": 413, "target": 251, "value": 28}, {"source": 413, "target": 255, "value": 28}, {"source": 413, "target": 257, "value": 29}, {"source": 413, "target": 261, "value": 6}, {"source": 413, "target": 263, "value": 1}, {"source": 413, "target": 273, "value": 7}, {"source": 413, "target": 275, "value": 6}, {"source": 413, "target": 278, "value": 6}, {"source": 413, "target": 279, "value": 29}, {"source": 413, "target": 287, "value": 4}, {"source": 413, "target": 309, "value": 8}, {"source": 413, "target": 321, "value": 7}, {"source": 413, "target": 323, "value": 8}, {"source": 413, "target": 339, "value": 7}, {"source": 413, "target": 351, "value": 7}, {"source": 413, "target": 363, "value": 1}, {"source": 413, "target": 365, "value": 8}, {"source": 413, "target": 375, "value": 8}, {"source": 413, "target": 378, "value": 8}, {"source": 413, "target": 401, "value": 8}, {"source": 413, "target": 405, "value": 9}, {"source": 423, "target": 219, "value": 10}, {"source": 423, "target": 221, "value": 8}, {"source": 423, "target": 248, "value": 8}, {"source": 423, "target": 257, "value": 9}, {"source": 423, "target": 261, "value": 8}, {"source": 423, "target": 273, "value": 1}, {"source": 423, "target": 309, "value": 8}, {"source": 423, "target": 323, "value": 2}, {"source": 423, "target": 347, "value": 8}, {"source": 423, "target": 348, "value": 8}, {"source": 423, "target": 369, "value": 10}, {"source": 423, "target": 398, "value": 9}, {"source": 423, "target": 413, "value": 8}, {"source": 425, "target": 225, "value": 2}, {"source": 425, "target": 228, "value": 8}, {"source": 425, "target": 275, "value": 4}, {"source": 425, "target": 278, "value": 8}, {"source": 425, "target": 287, "value": 8}, {"source": 425, "target": 321, "value": 8}, {"source": 425, "target": 365, "value": 9}, {"source": 425, "target": 375, "value": 3}, {"source": 425, "target": 377, "value": 9}, {"source": 425, "target": 378, "value": 8}, {"source": 425, "target": 413, "value": 8}, {"source": 428, "target": 225, "value": 8}, {"source": 428, "target": 228, "value": 4}, {"source": 428, "target": 249, "value": 8}, {"source": 428, "target": 275, "value": 8}, {"source": 428, "target": 278, "value": 4}, {"source": 428, "target": 303, "value": 9}, {"source": 428, "target": 321, "value": 8}, {"source": 428, "target": 353, "value": 9}, {"source": 428, "target": 365, "value": 9}, {"source": 428, "target": 375, "value": 7}, {"source": 428, "target": 378, "value": 3}, {"source": 428, "target": 399, "value": 8}, {"source": 428, "target": 413, "value": 8}, {"source": 428, "target": 425, "value": 8}, {"source": 429, "target": 221, "value": 11}, {"source": 429, "target": 225, "value": 8}, {"source": 429, "target": 233, "value": 11}, {"source": 429, "target": 243, "value": 9}, {"source": 429, "target": 255, "value": 8}, {"source": 429, "target": 279, "value": 4}, {"source": 429, "target": 287, "value": 9}, {"source": 429, "target": 291, "value": 8}, {"source": 429, "target": 293, "value": 8}, {"source": 429, "target": 305, "value": 8}, {"source": 429, "target": 317, "value": 8}, {"source": 429, "target": 333, "value": 11}, {"source": 429, "target": 335, "value": 9}, {"source": 429, "target": 339, "value": 9}, {"source": 429, "target": 341, "value": 8}, {"source": 429, "target": 377, "value": 4}, {"source": 429, "target": 381, "value": 9}, {"source": 429, "target": 383, "value": 10}, {"source": 429, "target": 393, "value": 8}, {"source": 429, "target": 405, "value": 8}, {"source": 429, "target": 425, "value": 9}, {"source": 431, "target": 221, "value": 10}, {"source": 431, "target": 231, "value": 4}, {"source": 431, "target": 243, "value": 9}, {"source": 431, "target": 273, "value": 10}, {"source": 431, "target": 281, "value": 2}, {"source": 431, "target": 293, "value": 9}, {"source": 431, "target": 333, "value": 8}, {"source": 431, "target": 369, "value": 7}, {"source": 431, "target": 377, "value": 7}, {"source": 431, "target": 381, "value": 2}, {"source": 431, "target": 393, "value": 9}, {"source": 431, "target": 398, "value": 8}, {"source": 435, "target": 273, "value": 8}, {"source": 435, "target": 285, "value": 4}, {"source": 435, "target": 335, "value": 4}, {"source": 435, "target": 377, "value": 9}, {"source": 437, "target": 225, "value": 8}, {"source": 437, "target": 228, "value": 8}, {"source": 437, "target": 249, "value": 8}, {"source": 437, "target": 263, "value": 9}, {"source": 437, "target": 278, "value": 8}, {"source": 437, "target": 285, "value": 8}, {"source": 437, "target": 287, "value": 4}, {"source": 437, "target": 293, "value": 9}, {"source": 437, "target": 303, "value": 10}, {"source": 437, "target": 347, "value": 9}, {"source": 437, "target": 353, "value": 10}, {"source": 437, "target": 363, "value": 9}, {"source": 437, "target": 378, "value": 8}, {"source": 437, "target": 399, "value": 8}, {"source": 437, "target": 413, "value": 4}, {"source": 437, "target": 425, "value": 8}, {"source": 437, "target": 428, "value": 7}, {"source": 437, "target": 429, "value": 9}, {"source": 441, "target": 291, "value": 4}, {"source": 441, "target": 293, "value": 5}, {"source": 441, "target": 303, "value": 8}, {"source": 441, "target": 341, "value": 2}, {"source": 441, "target": 353, "value": 8}, {"source": 441, "target": 365, "value": 8}, {"source": 441, "target": 381, "value": 9}, {"source": 441, "target": 429, "value": 8}, {"source": 443, "target": 243, "value": 2}, {"source": 443, "target": 281, "value": 8}, {"source": 443, "target": 293, "value": 1}, {"source": 443, "target": 317, "value": 4}, {"source": 443, "target": 333, "value": 9}, {"source": 443, "target": 335, "value": 10}, {"source": 443, "target": 339, "value": 10}, {"source": 443, "target": 377, "value": 9}, {"source": 443, "target": 381, "value": 8}, {"source": 443, "target": 393, "value": 1}, {"source": 443, "target": 398, "value": 9}, {"source": 443, "target": 429, "value": 8}, {"source": 443, "target": 431, "value": 9}, {"source": 453, "target": 215, "value": 4}, {"source": 453, "target": 227, "value": 4}, {"source": 453, "target": 228, "value": 8}, {"source": 453, "target": 249, "value": 10}, {"source": 453, "target": 263, "value": 9}, {"source": 453, "target": 278, "value": 8}, {"source": 453, "target": 279, "value": 4}, {"source": 453, "target": 293, "value": 10}, {"source": 453, "target": 303, "value": 1}, {"source": 453, "target": 315, "value": 4}, {"source": 453, "target": 341, "value": 8}, {"source": 453, "target": 353, "value": 0}, {"source": 453, "target": 363, "value": 9}, {"source": 453, "target": 365, "value": 4}, {"source": 453, "target": 377, "value": 4}, {"source": 453, "target": 378, "value": 8}, {"source": 453, "target": 399, "value": 9}, {"source": 453, "target": 401, "value": 8}, {"source": 453, "target": 405, "value": 3}, {"source": 453, "target": 413, "value": 9}, {"source": 453, "target": 428, "value": 8}, {"source": 453, "target": 437, "value": 9}, {"source": 453, "target": 441, "value": 8}, {"source": 455, "target": 245, "value": 10}, {"source": 455, "target": 251, "value": 8}, {"source": 455, "target": 255, "value": 4}, {"source": 455, "target": 263, "value": 9}, {"source": 455, "target": 293, "value": 8}, {"source": 455, "target": 305, "value": 4}, {"source": 455, "target": 363, "value": 9}, {"source": 455, "target": 377, "value": 11}, {"source": 455, "target": 393, "value": 8}, {"source": 455, "target": 401, "value": 8}, {"source": 455, "target": 405, "value": 3}, {"source": 455, "target": 413, "value": 9}, {"source": 455, "target": 429, "value": 8}, {"source": 455, "target": 453, "value": 7}, {"source": 459, "target": 263, "value": 11}, {"source": 459, "target": 309, "value": 4}, {"source": 459, "target": 333, "value": 8}, {"source": 459, "target": 351, "value": 10}, {"source": 459, "target": 363, "value": 10}, {"source": 459, "target": 405, "value": 7}, {"source": 459, "target": 413, "value": 10}, {"source": 461, "target": 215, "value": 8}, {"source": 461, "target": 221, "value": 9}, {"source": 461, "target": 249, "value": 8}, {"source": 461, "target": 261, "value": 2}, {"source": 461, "target": 263, "value": 8}, {"source": 461, "target": 273, "value": 8}, {"source": 461, "target": 285, "value": 8}, {"source": 461, "target": 287, "value": 8}, {"source": 461, "target": 309, "value": 4}, {"source": 461, "target": 311, "value": 4}, {"source": 461, "target": 315, "value": 8}, {"source": 461, "target": 321, "value": 9}, {"source": 461, "target": 323, "value": 8}, {"source": 461, "target": 333, "value": 8}, {"source": 461, "target": 339, "value": 10}, {"source": 461, "target": 353, "value": 8}, {"source": 461, "target": 363, "value": 4}, {"source": 461, "target": 365, "value": 7}, {"source": 461, "target": 371, "value": 9}, {"source": 461, "target": 405, "value": 9}, {"source": 461, "target": 411, "value": 3}, {"source": 461, "target": 413, "value": 2}, {"source": 461, "target": 423, "value": 8}, {"source": 461, "target": 437, "value": 8}, {"source": 461, "target": 453, "value": 4}, {"source": 461, "target": 459, "value": 8}, {"source": 465, "target": 215, "value": 4}, {"source": 465, "target": 227, "value": 8}, {"source": 465, "target": 315, "value": 4}, {"source": 465, "target": 317, "value": 9}, {"source": 465, "target": 353, "value": 8}, {"source": 465, "target": 365, "value": 2}, {"source": 465, "target": 377, "value": 8}, {"source": 465, "target": 405, "value": 9}, {"source": 465, "target": 453, "value": 4}, {"source": 465, "target": 461, "value": 7}, {"source": 467, "target": 243, "value": 9}, {"source": 467, "target": 255, "value": 10}, {"source": 467, "target": 293, "value": 4}, {"source": 467, "target": 305, "value": 8}, {"source": 467, "target": 317, "value": 4}, {"source": 467, "target": 363, "value": 8}, {"source": 467, "target": 393, "value": 4}, {"source": 467, "target": 405, "value": 7}, {"source": 467, "target": 443, "value": 9}, {"source": 471, "target": 221, "value": 4}, {"source": 471, "target": 261, "value": 10}, {"source": 471, "target": 293, "value": 11}, {"source": 471, "target": 309, "value": 8}, {"source": 471, "target": 321, "value": 4}, {"source": 471, "target": 371, "value": 4}, {"source": 471, "target": 461, "value": 8}, {"source": 473, "target": 219, "value": 9}, {"source": 473, "target": 221, "value": 4}, {"source": 473, "target": 248, "value": 8}, {"source": 473, "target": 257, "value": 9}, {"source": 473, "target": 261, "value": 8}, {"source": 473, "target": 273, "value": 1}, {"source": 473, "target": 285, "value": 8}, {"source": 473, "target": 309, "value": 8}, {"source": 473, "target": 323, "value": 1}, {"source": 473, "target": 335, "value": 8}, {"source": 473, "target": 347, "value": 4}, {"source": 473, "target": 348, "value": 8}, {"source": 473, "target": 369, "value": 10}, {"source": 473, "target": 377, "value": 9}, {"source": 473, "target": 398, "value": 8}, {"source": 473, "target": 413, "value": 9}, {"source": 473, "target": 423, "value": 1}, {"source": 473, "target": 435, "value": 8}, {"source": 473, "target": 461, "value": 8}, {"source": 483, "target": 221, "value": 4}, {"source": 483, "target": 233, "value": 1}, {"source": 483, "target": 257, "value": 8}, {"source": 483, "target": 279, "value": 4}, {"source": 483, "target": 317, "value": 9}, {"source": 483, "target": 321, "value": 4}, {"source": 483, "target": 333, "value": 1}, {"source": 483, "target": 335, "value": 10}, {"source": 483, "target": 381, "value": 8}, {"source": 483, "target": 383, "value": 2}, {"source": 483, "target": 405, "value": 10}, {"source": 483, "target": 407, "value": 8}, {"source": 483, "target": 429, "value": 9}, {"source": 485, "target": 233, "value": 4}, {"source": 485, "target": 245, "value": 9}, {"source": 485, "target": 248, "value": 9}, {"source": 485, "target": 273, "value": 8}, {"source": 485, "target": 285, "value": 2}, {"source": 485, "target": 293, "value": 8}, {"source": 485, "target": 333, "value": 8}, {"source": 485, "target": 335, "value": 4}, {"source": 485, "target": 345, "value": 9}, {"source": 485, "target": 347, "value": 8}, {"source": 485, "target": 348, "value": 9}, {"source": 485, "target": 377, "value": 10}, {"source": 485, "target": 395, "value": 9}, {"source": 485, "target": 398, "value": 9}, {"source": 485, "target": 435, "value": 3}, {"source": 485, "target": 437, "value": 9}, {"source": 485, "target": 473, "value": 8}, {"source": 489, "target": 243, "value": 3}, {"source": 489, "target": 251, "value": 8}, {"source": 489, "target": 263, "value": 8}, {"source": 489, "target": 281, "value": 8}, {"source": 489, "target": 287, "value": 9}, {"source": 489, "target": 293, "value": 2}, {"source": 489, "target": 317, "value": 10}, {"source": 489, "target": 333, "value": 10}, {"source": 489, "target": 335, "value": 8}, {"source": 489, "target": 339, "value": 4}, {"source": 489, "target": 341, "value": 9}, {"source": 489, "target": 351, "value": 8}, {"source": 489, "target": 363, "value": 8}, {"source": 489, "target": 377, "value": 8}, {"source": 489, "target": 381, "value": 8}, {"source": 489, "target": 393, "value": 2}, {"source": 489, "target": 398, "value": 9}, {"source": 489, "target": 401, "value": 8}, {"source": 489, "target": 413, "value": 2}, {"source": 489, "target": 429, "value": 9}, {"source": 489, "target": 431, "value": 8}, {"source": 489, "target": 437, "value": 8}, {"source": 489, "target": 443, "value": 3}, {"source": 489, "target": 461, "value": 4}, {"source": 489, "target": 467, "value": 10}, {"source": 491, "target": 291, "value": 4}, {"source": 491, "target": 293, "value": 10}, {"source": 491, "target": 341, "value": 4}, {"source": 491, "target": 365, "value": 8}, {"source": 491, "target": 381, "value": 9}, {"source": 491, "target": 429, "value": 7}, {"source": 491, "target": 441, "value": 3}, {"source": 495, "target": 233, "value": 4}, {"source": 495, "target": 245, "value": 4}, {"source": 495, "target": 248, "value": 8}, {"source": 495, "target": 285, "value": 10}, {"source": 495, "target": 333, "value": 8}, {"source": 495, "target": 345, "value": 4}, {"source": 495, "target": 348, "value": 8}, {"source": 495, "target": 395, "value": 4}, {"source": 495, "target": 398, "value": 8}, {"source": 495, "target": 485, "value": 8}, {"source": 497, "target": 221, "value": 8}, {"source": 497, "target": 263, "value": 5}, {"source": 497, "target": 273, "value": 4}, {"source": 497, "target": 285, "value": 8}, {"source": 497, "target": 293, "value": 8}, {"source": 497, "target": 309, "value": 4}, {"source": 497, "target": 323, "value": 9}, {"source": 497, "target": 347, "value": 4}, {"source": 497, "target": 351, "value": 4}, {"source": 497, "target": 363, "value": 4}, {"source": 497, "target": 405, "value": 4}, {"source": 497, "target": 413, "value": 4}, {"source": 497, "target": 423, "value": 9}, {"source": 497, "target": 437, "value": 9}, {"source": 497, "target": 459, "value": 4}, {"source": 497, "target": 473, "value": 4}, {"source": 497, "target": 485, "value": 8}, {"source": 498, "target": 219, "value": 8}, {"source": 498, "target": 233, "value": 4}, {"source": 498, "target": 245, "value": 8}, {"source": 498, "target": 248, "value": 4}, {"source": 498, "target": 257, "value": 8}, {"source": 498, "target": 273, "value": 10}, {"source": 498, "target": 285, "value": 10}, {"source": 498, "target": 309, "value": 9}, {"source": 498, "target": 323, "value": 10}, {"source": 498, "target": 333, "value": 9}, {"source": 498, "target": 345, "value": 8}, {"source": 498, "target": 348, "value": 4}, {"source": 498, "target": 369, "value": 8}, {"source": 498, "target": 395, "value": 8}, {"source": 498, "target": 398, "value": 4}, {"source": 498, "target": 423, "value": 9}, {"source": 498, "target": 473, "value": 9}, {"source": 498, "target": 485, "value": 9}, {"source": 498, "target": 495, "value": 8}, {"source": 501, "target": 249, "value": 8}, {"source": 501, "target": 251, "value": 4}, {"source": 501, "target": 261, "value": 9}, {"source": 501, "target": 263, "value": 8}, {"source": 501, "target": 311, "value": 9}, {"source": 501, "target": 341, "value": 10}, {"source": 501, "target": 351, "value": 4}, {"source": 501, "target": 363, "value": 8}, {"source": 501, "target": 401, "value": 2}, {"source": 501, "target": 411, "value": 9}, {"source": 501, "target": 413, "value": 8}, {"source": 501, "target": 453, "value": 8}, {"source": 501, "target": 455, "value": 8}, {"source": 501, "target": 461, "value": 9}, {"source": 501, "target": 489, "value": 8}, {"source": 503, "target": 227, "value": 8}, {"source": 503, "target": 228, "value": 8}, {"source": 503, "target": 245, "value": 8}, {"source": 503, "target": 249, "value": 9}, {"source": 503, "target": 251, "value": 9}, {"source": 503, "target": 257, "value": 8}, {"source": 503, "target": 278, "value": 8}, {"source": 503, "target": 279, "value": 9}, {"source": 503, "target": 293, "value": 10}, {"source": 503, "target": 303, "value": 2}, {"source": 503, "target": 341, "value": 8}, {"source": 503, "target": 345, "value": 8}, {"source": 503, "target": 351, "value": 9}, {"source": 503, "target": 353, "value": 1}, {"source": 503, "target": 377, "value": 8}, {"source": 503, "target": 378, "value": 8}, {"source": 503, "target": 399, "value": 9}, {"source": 503, "target": 401, "value": 9}, {"source": 503, "target": 405, "value": 9}, {"source": 503, "target": 407, "value": 7}, {"source": 503, "target": 428, "value": 8}, {"source": 503, "target": 437, "value": 9}, {"source": 503, "target": 441, "value": 8}, {"source": 503, "target": 453, "value": 1}, {"source": 503, "target": 501, "value": 8}, {"source": 513, "target": 215, "value": 26}, {"source": 513, "target": 219, "value": 27}, {"source": 513, "target": 221, "value": 26}, {"source": 513, "target": 225, "value": 6}, {"source": 513, "target": 227, "value": 29}, {"source": 513, "target": 228, "value": 6}, {"source": 513, "target": 231, "value": 28}, {"source": 513, "target": 233, "value": 29}, {"source": 513, "target": 243, "value": 29}, {"source": 513, "target": 245, "value": 28}, {"source": 513, "target": 248, "value": 29}, {"source": 513, "target": 249, "value": 29}, {"source": 513, "target": 251, "value": 28}, {"source": 513, "target": 255, "value": 28}, {"source": 513, "target": 257, "value": 29}, {"source": 513, "target": 261, "value": 29}, {"source": 513, "target": 263, "value": 1}, {"source": 513, "target": 273, "value": 28}, {"source": 513, "target": 275, "value": 6}, {"source": 513, "target": 278, "value": 6}, {"source": 513, "target": 287, "value": 3}, {"source": 513, "target": 309, "value": 8}, {"source": 513, "target": 321, "value": 7}, {"source": 513, "target": 351, "value": 7}, {"source": 513, "target": 363, "value": 1}, {"source": 513, "target": 365, "value": 9}, {"source": 513, "target": 375, "value": 8}, {"source": 513, "target": 378, "value": 8}, {"source": 513, "target": 401, "value": 8}, {"source": 513, "target": 405, "value": 9}, {"source": 513, "target": 413, "value": 0}, {"source": 513, "target": 425, "value": 8}, {"source": 513, "target": 428, "value": 8}, {"source": 513, "target": 437, "value": 4}, {"source": 513, "target": 453, "value": 9}, {"source": 513, "target": 455, "value": 9}, {"source": 513, "target": 459, "value": 9}, {"source": 513, "target": 461, "value": 4}, {"source": 513, "target": 489, "value": 4}, {"source": 513, "target": 497, "value": 4}, {"source": 513, "target": 501, "value": 8}, {"source": 515, "target": 215, "value": 4}, {"source": 515, "target": 315, "value": 4}, {"source": 515, "target": 353, "value": 8}, {"source": 515, "target": 365, "value": 4}, {"source": 515, "target": 405, "value": 9}, {"source": 515, "target": 453, "value": 4}, {"source": 515, "target": 461, "value": 7}, {"source": 515, "target": 465, "value": 3}, {"source": 519, "target": 219, "value": 4}, {"source": 519, "target": 221, "value": 9}, {"source": 519, "target": 231, "value": 8}, {"source": 519, "target": 248, "value": 9}, {"source": 519, "target": 257, "value": 8}, {"source": 519, "target": 273, "value": 5}, {"source": 519, "target": 281, "value": 8}, {"source": 519, "target": 309, "value": 10}, {"source": 519, "target": 321, "value": 10}, {"source": 519, "target": 323, "value": 11}, {"source": 519, "target": 348, "value": 9}, {"source": 519, "target": 369, "value": 2}, {"source": 519, "target": 377, "value": 8}, {"source": 519, "target": 381, "value": 8}, {"source": 519, "target": 398, "value": 8}, {"source": 519, "target": 423, "value": 10}, {"source": 519, "target": 431, "value": 8}, {"source": 519, "target": 473, "value": 10}, {"source": 519, "target": 498, "value": 8}, {"source": 521, "target": 221, "value": 2}, {"source": 521, "target": 233, "value": 8}, {"source": 521, "target": 261, "value": 10}, {"source": 521, "target": 279, "value": 8}, {"source": 521, "target": 293, "value": 11}, {"source": 521, "target": 309, "value": 8}, {"source": 521, "target": 321, "value": 2}, {"source": 521, "target": 333, "value": 8}, {"source": 521, "target": 371, "value": 4}, {"source": 521, "target": 383, "value": 8}, {"source": 521, "target": 461, "value": 9}, {"source": 521, "target": 471, "value": 3}, {"source": 521, "target": 483, "value": 8}, {"source": 525, "target": 225, "value": 2}, {"source": 525, "target": 228, "value": 8}, {"source": 525, "target": 251, "value": 8}, {"source": 525, "target": 273, "value": 8}, {"source": 525, "target": 275, "value": 4}, {"source": 525, "target": 278, "value": 8}, {"source": 525, "target": 285, "value": 9}, {"source": 525, "target": 287, "value": 8}, {"source": 525, "target": 321, "value": 8}, {"source": 525, "target": 335, "value": 9}, {"source": 525, "target": 351, "value": 8}, {"source": 525, "target": 365, "value": 10}, {"source": 525, "target": 375, "value": 4}, {"source": 525, "target": 377, "value": 4}, {"source": 525, "target": 378, "value": 8}, {"source": 525, "target": 401, "value": 8}, {"source": 525, "target": 413, "value": 8}, {"source": 525, "target": 425, "value": 2}, {"source": 525, "target": 428, "value": 8}, {"source": 525, "target": 429, "value": 9}, {"source": 525, "target": 435, "value": 9}, {"source": 525, "target": 437, "value": 8}, {"source": 525, "target": 473, "value": 8}, {"source": 525, "target": 485, "value": 9}, {"source": 525, "target": 501, "value": 8}, {"source": 525, "target": 503, "value": 8}, {"source": 525, "target": 513, "value": 8}, {"source": 527, "target": 227, "value": 4}, {"source": 527, "target": 279, "value": 10}, {"source": 527, "target": 303, "value": 9}, {"source": 527, "target": 317, "value": 10}, {"source": 527, "target": 353, "value": 4}, {"source": 527, "target": 365, "value": 4}, {"source": 527, "target": 377, "value": 4}, {"source": 527, "target": 453, "value": 4}, {"source": 527, "target": 465, "value": 7}, {"source": 527, "target": 503, "value": 8}, {"source": 528, "target": 225, "value": 8}, {"source": 528, "target": 228, "value": 4}, {"source": 528, "target": 249, "value": 8}, {"source": 528, "target": 275, "value": 8}, {"source": 528, "target": 278, "value": 4}, {"source": 528, "target": 303, "value": 10}, {"source": 528, "target": 321, "value": 8}, {"source": 528, "target": 353, "value": 10}, {"source": 528, "target": 365, "value": 10}, {"source": 528, "target": 375, "value": 8}, {"source": 528, "target": 378, "value": 4}, {"source": 528, "target": 399, "value": 8}, {"source": 528, "target": 413, "value": 8}, {"source": 528, "target": 425, "value": 8}, {"source": 528, "target": 428, "value": 4}, {"source": 528, "target": 437, "value": 7}, {"source": 528, "target": 453, "value": 9}, {"source": 528, "target": 503, "value": 8}, {"source": 528, "target": 513, "value": 8}, {"source": 528, "target": 525, "value": 8}, {"source": 531, "target": 221, "value": 10}, {"source": 531, "target": 231, "value": 4}, {"source": 531, "target": 273, "value": 4}, {"source": 531, "target": 279, "value": 9}, {"source": 531, "target": 281, "value": 4}, {"source": 531, "target": 285, "value": 8}, {"source": 531, "target": 335, "value": 8}, {"source": 531, "target": 369, "value": 8}, {"source": 531, "target": 377, "value": 4}, {"source": 531, "target": 381, "value": 4}, {"source": 531, "target": 429, "value": 9}, {"source": 531, "target": 431, "value": 4}, {"source": 531, "target": 435, "value": 7}, {"source": 531, "target": 473, "value": 8}, {"source": 531, "target": 485, "value": 7}, {"source": 531, "target": 519, "value": 8}, {"source": 531, "target": 525, "value": 9}, {"source": 533, "target": 221, "value": 4}, {"source": 533, "target": 233, "value": 0}, {"source": 533, "target": 243, "value": 9}, {"source": 533, "target": 245, "value": 8}, {"source": 533, "target": 248, "value": 8}, {"source": 533, "target": 257, "value": 4}, {"source": 533, "target": 279, "value": 4}, {"source": 533, "target": 281, "value": 8}, {"source": 533, "target": 285, "value": 9}, {"source": 533, "target": 291, "value": 9}, {"source": 533, "target": 293, "value": 9}, {"source": 533, "target": 317, "value": 8}, {"source": 533, "target": 321, "value": 4}, {"source": 533, "target": 333, "value": 0}, {"source": 533, "target": 335, "value": 10}, {"source": 533, "target": 341, "value": 9}, {"source": 533, "target": 345, "value": 8}, {"source": 533, "target": 348, "value": 8}, {"source": 533, "target": 381, "value": 2}, {"source": 533, "target": 383, "value": 1}, {"source": 533, "target": 393, "value": 9}, {"source": 533, "target": 395, "value": 8}, {"source": 533, "target": 398, "value": 4}, {"source": 533, "target": 405, "value": 11}, {"source": 533, "target": 407, "value": 4}, {"source": 533, "target": 429, "value": 4}, {"source": 533, "target": 431, "value": 8}, {"source": 533, "target": 441, "value": 9}, {"source": 533, "target": 443, "value": 9}, {"source": 533, "target": 483, "value": 1}, {"source": 533, "target": 485, "value": 8}, {"source": 533, "target": 489, "value": 8}, {"source": 533, "target": 491, "value": 9}, {"source": 533, "target": 495, "value": 8}, {"source": 533, "target": 498, "value": 8}, {"source": 533, "target": 521, "value": 8}, {"source": 543, "target": 243, "value": 2}, {"source": 543, "target": 281, "value": 8}, {"source": 543, "target": 293, "value": 1}, {"source": 543, "target": 317, "value": 4}, {"source": 543, "target": 333, "value": 10}, {"source": 543, "target": 335, "value": 9}, {"source": 543, "target": 339, "value": 9}, {"source": 543, "target": 377, "value": 8}, {"source": 543, "target": 381, "value": 8}, {"source": 543, "target": 393, "value": 1}, {"source": 543, "target": 398, "value": 9}, {"source": 543, "target": 429, "value": 7}, {"source": 543, "target": 431, "value": 9}, {"source": 543, "target": 443, "value": 2}, {"source": 543, "target": 467, "value": 8}, {"source": 543, "target": 489, "value": 2}, {"source": 543, "target": 533, "value": 8}, {"source": 545, "target": 228, "value": 9}, {"source": 545, "target": 233, "value": 4}, {"source": 545, "target": 243, "value": 9}, {"source": 545, "target": 245, "value": 2}, {"source": 545, "target": 248, "value": 8}, {"source": 545, "target": 249, "value": 8}, {"source": 545, "target": 257, "value": 8}, {"source": 545, "target": 273, "value": 10}, {"source": 545, "target": 278, "value": 9}, {"source": 545, "target": 285, "value": 10}, {"source": 545, "target": 293, "value": 9}, {"source": 545, "target": 303, "value": 11}, {"source": 545, "target": 317, "value": 8}, {"source": 545, "target": 323, "value": 10}, {"source": 545, "target": 333, "value": 8}, {"source": 545, "target": 345, "value": 2}, {"source": 545, "target": 348, "value": 8}, {"source": 545, "target": 353, "value": 11}, {"source": 545, "target": 378, "value": 8}, {"source": 545, "target": 393, "value": 9}, {"source": 545, "target": 395, "value": 4}, {"source": 545, "target": 398, "value": 8}, {"source": 545, "target": 399, "value": 8}, {"source": 545, "target": 407, "value": 8}, {"source": 545, "target": 423, "value": 9}, {"source": 545, "target": 428, "value": 8}, {"source": 545, "target": 437, "value": 8}, {"source": 545, "target": 443, "value": 9}, {"source": 545, "target": 453, "value": 9}, {"source": 545, "target": 473, "value": 9}, {"source": 545, "target": 485, "value": 9}, {"source": 545, "target": 495, "value": 3}, {"source": 545, "target": 498, "value": 8}, {"source": 545, "target": 503, "value": 4}, {"source": 545, "target": 528, "value": 8}, {"source": 545, "target": 533, "value": 8}, {"source": 545, "target": 543, "value": 8}, {"source": 548, "target": 219, "value": 8}, {"source": 548, "target": 233, "value": 4}, {"source": 548, "target": 245, "value": 8}, {"source": 548, "target": 248, "value": 4}, {"source": 548, "target": 257, "value": 8}, {"source": 548, "target": 273, "value": 10}, {"source": 548, "target": 285, "value": 10}, {"source": 548, "target": 309, "value": 9}, {"source": 548, "target": 323, "value": 10}, {"source": 548, "target": 333, "value": 9}, {"source": 548, "target": 345, "value": 8}, {"source": 548, "target": 348, "value": 4}, {"source": 548, "target": 369, "value": 8}, {"source": 548, "target": 395, "value": 8}, {"source": 548, "target": 398, "value": 4}, {"source": 548, "target": 423, "value": 9}, {"source": 548, "target": 473, "value": 9}, {"source": 548, "target": 485, "value": 9}, {"source": 548, "target": 495, "value": 7}, {"source": 548, "target": 498, "value": 3}, {"source": 548, "target": 519, "value": 8}, {"source": 548, "target": 533, "value": 8}, {"source": 548, "target": 545, "value": 8}, {"source": 549, "target": 228, "value": 9}, {"source": 549, "target": 249, "value": 2}, {"source": 549, "target": 251, "value": 10}, {"source": 549, "target": 261, "value": 8}, {"source": 549, "target": 263, "value": 9}, {"source": 549, "target": 278, "value": 9}, {"source": 549, "target": 303, "value": 11}, {"source": 549, "target": 309, "value": 9}, {"source": 549, "target": 311, "value": 8}, {"source": 549, "target": 321, "value": 11}, {"source": 549, "target": 351, "value": 8}, {"source": 549, "target": 353, "value": 10}, {"source": 549, "target": 363, "value": 9}, {"source": 549, "target": 378, "value": 8}, {"source": 549, "target": 399, "value": 4}, {"source": 549, "target": 405, "value": 9}, {"source": 549, "target": 411, "value": 8}, {"source": 549, "target": 413, "value": 8}, {"source": 549, "target": 428, "value": 8}, {"source": 549, "target": 437, "value": 8}, {"source": 549, "target": 453, "value": 10}, {"source": 549, "target": 459, "value": 8}, {"source": 549, "target": 461, "value": 8}, {"source": 549, "target": 497, "value": 4}, {"source": 549, "target": 501, "value": 8}, {"source": 549, "target": 503, "value": 9}, {"source": 549, "target": 513, "value": 8}, {"source": 549, "target": 528, "value": 8}, {"source": 549, "target": 545, "value": 8}, {"source": 551, "target": 251, "value": 4}, {"source": 551, "target": 341, "value": 10}, {"source": 551, "target": 351, "value": 4}, {"source": 551, "target": 401, "value": 4}, {"source": 551, "target": 489, "value": 7}, {"source": 551, "target": 501, "value": 3}, {"source": 551, "target": 503, "value": 9}, {"source": 551, "target": 525, "value": 8}, {"source": 555, "target": 245, "value": 10}, {"source": 555, "target": 251, "value": 8}, {"source": 555, "target": 255, "value": 4}, {"source": 555, "target": 293, "value": 8}, {"source": 555, "target": 305, "value": 4}, {"source": 555, "target": 377, "value": 11}, {"source": 555, "target": 393, "value": 8}, {"source": 555, "target": 405, "value": 4}, {"source": 555, "target": 429, "value": 8}, {"source": 555, "target": 455, "value": 4}, {"source": 557, "target": 219, "value": 8}, {"source": 557, "target": 233, "value": 3}, {"source": 557, "target": 245, "value": 8}, {"source": 557, "target": 248, "value": 8}, {"source": 557, "target": 255, "value": 8}, {"source": 557, "target": 257, "value": 2}, {"source": 557, "target": 273, "value": 11}, {"source": 557, "target": 305, "value": 8}, {"source": 557, "target": 309, "value": 9}, {"source": 557, "target": 317, "value": 9}, {"source": 557, "target": 323, "value": 10}, {"source": 557, "target": 333, "value": 3}, {"source": 557, "target": 335, "value": 9}, {"source": 557, "target": 345, "value": 8}, {"source": 557, "target": 348, "value": 8}, {"source": 557, "target": 363, "value": 9}, {"source": 557, "target": 369, "value": 8}, {"source": 557, "target": 381, "value": 8}, {"source": 557, "target": 383, "value": 4}, {"source": 557, "target": 398, "value": 8}, {"source": 557, "target": 405, "value": 4}, {"source": 557, "target": 407, "value": 4}, {"source": 557, "target": 423, "value": 9}, {"source": 557, "target": 467, "value": 9}, {"source": 557, "target": 473, "value": 9}, {"source": 557, "target": 483, "value": 4}, {"source": 557, "target": 498, "value": 8}, {"source": 557, "target": 503, "value": 7}, {"source": 557, "target": 519, "value": 8}, {"source": 557, "target": 533, "value": 2}, {"source": 557, "target": 545, "value": 8}, {"source": 557, "target": 548, "value": 7}, {"source": 561, "target": 249, "value": 8}, {"source": 561, "target": 261, "value": 2}, {"source": 561, "target": 273, "value": 8}, {"source": 561, "target": 285, "value": 8}, {"source": 561, "target": 311, "value": 4}, {"source": 561, "target": 323, "value": 8}, {"source": 561, "target": 363, "value": 10}, {"source": 561, "target": 411, "value": 4}, {"source": 561, "target": 413, "value": 9}, {"source": 561, "target": 423, "value": 8}, {"source": 561, "target": 461, "value": 2}, {"source": 561, "target": 473, "value": 8}, {"source": 561, "target": 501, "value": 9}, {"source": 561, "target": 549, "value": 8}, {"source": 563, "target": 225, "value": 8}, {"source": 563, "target": 228, "value": 9}, {"source": 563, "target": 263, "value": 2}, {"source": 563, "target": 275, "value": 8}, {"source": 563, "target": 278, "value": 9}, {"source": 563, "target": 287, "value": 8}, {"source": 563, "target": 309, "value": 9}, {"source": 563, "target": 321, "value": 8}, {"source": 563, "target": 351, "value": 8}, {"source": 563, "target": 363, "value": 2}, {"source": 563, "target": 365, "value": 9}, {"source": 563, "target": 375, "value": 8}, {"source": 563, "target": 378, "value": 9}, {"source": 563, "target": 401, "value": 8}, {"source": 563, "target": 405, "value": 9}, {"source": 563, "target": 413, "value": 1}, {"source": 563, "target": 425, "value": 8}, {"source": 563, "target": 428, "value": 9}, {"source": 563, "target": 437, "value": 8}, {"source": 563, "target": 453, "value": 9}, {"source": 563, "target": 455, "value": 10}, {"source": 563, "target": 459, "value": 9}, {"source": 563, "target": 461, "value": 8}, {"source": 563, "target": 489, "value": 8}, {"source": 563, "target": 497, "value": 4}, {"source": 563, "target": 501, "value": 8}, {"source": 563, "target": 513, "value": 1}, {"source": 563, "target": 525, "value": 8}, {"source": 563, "target": 528, "value": 8}, {"source": 563, "target": 549, "value": 8}, {"source": 573, "target": 219, "value": 9}, {"source": 573, "target": 221, "value": 2}, {"source": 573, "target": 227, "value": 8}, {"source": 573, "target": 233, "value": 8}, {"source": 573, "target": 248, "value": 8}, {"source": 573, "target": 249, "value": 8}, {"source": 573, "target": 251, "value": 4}, {"source": 573, "target": 257, "value": 8}, {"source": 573, "target": 261, "value": 8}, {"source": 573, "target": 273, "value": 1}, {"source": 573, "target": 279, "value": 8}, {"source": 573, "target": 285, "value": 8}, {"source": 573, "target": 293, "value": 10}, {"source": 573, "target": 309, "value": 8}, {"source": 573, "target": 317, "value": 10}, {"source": 573, "target": 321, "value": 2}, {"source": 573, "target": 323, "value": 1}, {"source": 573, "target": 333, "value": 9}, {"source": 573, "target": 335, "value": 8}, {"source": 573, "target": 347, "value": 4}, {"source": 573, "target": 348, "value": 8}, {"source": 573, "target": 351, "value": 9}, {"source": 573, "target": 365, "value": 4}, {"source": 573, "target": 369, "value": 9}, {"source": 573, "target": 371, "value": 9}, {"source": 573, "target": 377, "value": 4}, {"source": 573, "target": 383, "value": 9}, {"source": 573, "target": 398, "value": 8}, {"source": 573, "target": 399, "value": 8}, {"source": 573, "target": 401, "value": 9}, {"source": 573, "target": 413, "value": 9}, {"source": 573, "target": 423, "value": 1}, {"source": 573, "target": 435, "value": 8}, {"source": 573, "target": 461, "value": 8}, {"source": 573, "target": 465, "value": 8}, {"source": 573, "target": 471, "value": 9}, {"source": 573, "target": 473, "value": 1}, {"source": 573, "target": 483, "value": 8}, {"source": 573, "target": 485, "value": 8}, {"source": 573, "target": 497, "value": 4}, {"source": 573, "target": 498, "value": 8}, {"source": 573, "target": 501, "value": 9}, {"source": 573, "target": 503, "value": 7}, {"source": 573, "target": 519, "value": 9}, {"source": 573, "target": 521, "value": 4}, {"source": 573, "target": 525, "value": 4}, {"source": 573, "target": 527, "value": 7}, {"source": 573, "target": 531, "value": 7}, {"source": 573, "target": 533, "value": 9}, {"source": 573, "target": 545, "value": 9}, {"source": 573, "target": 548, "value": 8}, {"source": 573, "target": 549, "value": 8}, {"source": 573, "target": 551, "value": 9}, {"source": 573, "target": 557, "value": 8}, {"source": 573, "target": 561, "value": 8}, {"source": 575, "target": 225, "value": 4}, {"source": 575, "target": 228, "value": 8}, {"source": 575, "target": 275, "value": 4}, {"source": 575, "target": 278, "value": 8}, {"source": 575, "target": 321, "value": 8}, {"source": 575, "target": 365, "value": 10}, {"source": 575, "target": 375, "value": 4}, {"source": 575, "target": 377, "value": 10}, {"source": 575, "target": 378, "value": 8}, {"source": 575, "target": 413, "value": 8}, {"source": 575, "target": 425, "value": 4}, {"source": 575, "target": 428, "value": 8}, {"source": 575, "target": 513, "value": 8}, {"source": 575, "target": 525, "value": 3}, {"source": 575, "target": 528, "value": 8}, {"source": 575, "target": 563, "value": 8}, {"source": 578, "target": 225, "value": 8}, {"source": 578, "target": 228, "value": 4}, {"source": 578, "target": 249, "value": 8}, {"source": 578, "target": 275, "value": 8}, {"source": 578, "target": 278, "value": 4}, {"source": 578, "target": 303, "value": 10}, {"source": 578, "target": 321, "value": 8}, {"source": 578, "target": 353, "value": 10}, {"source": 578, "target": 365, "value": 10}, {"source": 578, "target": 375, "value": 8}, {"source": 578, "target": 378, "value": 4}, {"source": 578, "target": 399, "value": 8}, {"source": 578, "target": 413, "value": 8}, {"source": 578, "target": 425, "value": 8}, {"source": 578, "target": 428, "value": 4}, {"source": 578, "target": 437, "value": 8}, {"source": 578, "target": 453, "value": 9}, {"source": 578, "target": 503, "value": 8}, {"source": 578, "target": 513, "value": 8}, {"source": 578, "target": 525, "value": 7}, {"source": 578, "target": 528, "value": 3}, {"source": 578, "target": 545, "value": 8}, {"source": 578, "target": 549, "value": 8}, {"source": 578, "target": 563, "value": 9}, {"source": 578, "target": 575, "value": 8}, {"source": 579, "target": 221, "value": 11}, {"source": 579, "target": 233, "value": 12}, {"source": 579, "target": 279, "value": 4}, {"source": 579, "target": 317, "value": 8}, {"source": 579, "target": 333, "value": 11}, {"source": 579, "target": 383, "value": 10}, {"source": 579, "target": 429, "value": 4}, {"source": 579, "target": 483, "value": 10}, {"source": 579, "target": 531, "value": 9}, {"source": 579, "target": 533, "value": 9}, {"source": 581, "target": 221, "value": 11}, {"source": 581, "target": 231, "value": 4}, {"source": 581, "target": 243, "value": 8}, {"source": 581, "target": 273, "value": 11}, {"source": 581, "target": 281, "value": 2}, {"source": 581, "target": 291, "value": 8}, {"source": 581, "target": 293, "value": 8}, {"source": 581, "target": 333, "value": 9}, {"source": 581, "target": 341, "value": 8}, {"source": 581, "target": 369, "value": 8}, {"source": 581, "target": 377, "value": 8}, {"source": 581, "target": 381, "value": 2}, {"source": 581, "target": 393, "value": 8}, {"source": 581, "target": 398, "value": 9}, {"source": 581, "target": 429, "value": 8}, {"source": 581, "target": 431, "value": 2}, {"source": 581, "target": 441, "value": 9}, {"source": 581, "target": 443, "value": 8}, {"source": 581, "target": 489, "value": 7}, {"source": 581, "target": 491, "value": 8}, {"source": 581, "target": 519, "value": 8}, {"source": 581, "target": 531, "value": 3}, {"source": 581, "target": 533, "value": 4}, {"source": 581, "target": 543, "value": 8}, {"source": 585, "target": 243, "value": 9}, {"source": 585, "target": 273, "value": 8}, {"source": 585, "target": 281, "value": 8}, {"source": 585, "target": 285, "value": 2}, {"source": 585, "target": 293, "value": 2}, {"source": 585, "target": 333, "value": 8}, {"source": 585, "target": 335, "value": 4}, {"source": 585, "target": 347, "value": 4}, {"source": 585, "target": 377, "value": 10}, {"source": 585, "target": 381, "value": 8}, {"source": 585, "target": 393, "value": 9}, {"source": 585, "target": 398, "value": 8}, {"source": 585, "target": 431, "value": 8}, {"source": 585, "target": 435, "value": 4}, {"source": 585, "target": 437, "value": 4}, {"source": 585, "target": 443, "value": 9}, {"source": 585, "target": 473, "value": 8}, {"source": 585, "target": 485, "value": 2}, {"source": 585, "target": 489, "value": 9}, {"source": 585, "target": 497, "value": 4}, {"source": 585, "target": 525, "value": 9}, {"source": 585, "target": 531, "value": 7}, {"source": 585, "target": 533, "value": 7}, {"source": 585, "target": 543, "value": 9}, {"source": 585, "target": 573, "value": 8}, {"source": 585, "target": 581, "value": 8}, {"source": 587, "target": 225, "value": 9}, {"source": 587, "target": 263, "value": 9}, {"source": 587, "target": 285, "value": 9}, {"source": 587, "target": 287, "value": 4}, {"source": 587, "target": 291, "value": 8}, {"source": 587, "target": 335, "value": 9}, {"source": 587, "target": 341, "value": 8}, {"source": 587, "target": 363, "value": 9}, {"source": 587, "target": 381, "value": 10}, {"source": 587, "target": 413, "value": 4}, {"source": 587, "target": 425, "value": 8}, {"source": 587, "target": 429, "value": 4}, {"source": 587, "target": 435, "value": 9}, {"source": 587, "target": 437, "value": 4}, {"source": 587, "target": 441, "value": 8}, {"source": 587, "target": 461, "value": 8}, {"source": 587, "target": 485, "value": 9}, {"source": 587, "target": 489, "value": 9}, {"source": 587, "target": 491, "value": 8}, {"source": 587, "target": 513, "value": 4}, {"source": 587, "target": 525, "value": 7}, {"source": 587, "target": 533, "value": 9}, {"source": 587, "target": 563, "value": 8}, {"source": 587, "target": 581, "value": 8}, {"source": 587, "target": 585, "value": 8}, {"source": 591, "target": 291, "value": 4}, {"source": 591, "target": 293, "value": 10}, {"source": 591, "target": 341, "value": 4}, {"source": 591, "target": 365, "value": 8}, {"source": 591, "target": 381, "value": 10}, {"source": 591, "target": 429, "value": 8}, {"source": 591, "target": 441, "value": 4}, {"source": 591, "target": 491, "value": 4}, {"source": 591, "target": 533, "value": 9}, {"source": 591, "target": 581, "value": 8}, {"source": 591, "target": 587, "value": 8}, {"source": 593, "target": 243, "value": 1}, {"source": 593, "target": 245, "value": 10}, {"source": 593, "target": 251, "value": 8}, {"source": 593, "target": 255, "value": 8}, {"source": 593, "target": 281, "value": 8}, {"source": 593, "target": 293, "value": 1}, {"source": 593, "target": 305, "value": 8}, {"source": 593, "target": 317, "value": 2}, {"source": 593, "target": 333, "value": 10}, {"source": 593, "target": 335, "value": 9}, {"source": 593, "target": 339, "value": 9}, {"source": 593, "target": 377, "value": 8}, {"source": 593, "target": 381, "value": 8}, {"source": 593, "target": 393, "value": 1}, {"source": 593, "target": 398, "value": 9}, {"source": 593, "target": 405, "value": 8}, {"source": 593, "target": 429, "value": 8}, {"source": 593, "target": 431, "value": 9}, {"source": 593, "target": 443, "value": 1}, {"source": 593, "target": 455, "value": 8}, {"source": 593, "target": 467, "value": 4}, {"source": 593, "target": 489, "value": 2}, {"source": 593, "target": 533, "value": 8}, {"source": 593, "target": 543, "value": 1}, {"source": 593, "target": 545, "value": 9}, {"source": 593, "target": 555, "value": 8}, {"source": 593, "target": 581, "value": 8}, {"source": 593, "target": 585, "value": 8}, {"source": 603, "target": 227, "value": 8}, {"source": 603, "target": 228, "value": 8}, {"source": 603, "target": 249, "value": 9}, {"source": 603, "target": 278, "value": 8}, {"source": 603, "target": 279, "value": 9}, {"source": 603, "target": 293, "value": 10}, {"source": 603, "target": 303, "value": 2}, {"source": 603, "target": 341, "value": 8}, {"source": 603, "target": 353, "value": 1}, {"source": 603, "target": 377, "value": 8}, {"source": 603, "target": 378, "value": 8}, {"source": 603, "target": 399, "value": 9}, {"source": 603, "target": 405, "value": 9}, {"source": 603, "target": 428, "value": 8}, {"source": 603, "target": 437, "value": 8}, {"source": 603, "target": 441, "value": 8}, {"source": 603, "target": 453, "value": 1}, {"source": 603, "target": 503, "value": 2}, {"source": 603, "target": 527, "value": 8}, {"source": 603, "target": 528, "value": 8}, {"source": 603, "target": 545, "value": 9}, {"source": 603, "target": 549, "value": 9}, {"source": 603, "target": 578, "value": 8}, {"source": 605, "target": 215, "value": 8}, {"source": 605, "target": 231, "value": 8}, {"source": 605, "target": 245, "value": 10}, {"source": 605, "target": 251, "value": 8}, {"source": 605, "target": 255, "value": 2}, {"source": 605, "target": 281, "value": 8}, {"source": 605, "target": 293, "value": 8}, {"source": 605, "target": 305, "value": 2}, {"source": 605, "target": 315, "value": 8}, {"source": 605, "target": 317, "value": 8}, {"source": 605, "target": 353, "value": 8}, {"source": 605, "target": 363, "value": 8}, {"source": 605, "target": 365, "value": 8}, {"source": 605, "target": 377, "value": 11}, {"source": 605, "target": 381, "value": 8}, {"source": 605, "target": 393, "value": 8}, {"source": 605, "target": 405, "value": 2}, {"source": 605, "target": 429, "value": 8}, {"source": 605, "target": 431, "value": 8}, {"source": 605, "target": 453, "value": 4}, {"source": 605, "target": 455, "value": 4}, {"source": 605, "target": 461, "value": 8}, {"source": 605, "target": 465, "value": 9}, {"source": 605, "target": 467, "value": 8}, {"source": 605, "target": 515, "value": 8}, {"source": 605, "target": 531, "value": 8}, {"source": 605, "target": 555, "value": 3}, {"source": 605, "target": 557, "value": 8}, {"source": 605, "target": 581, "value": 8}, {"source": 605, "target": 593, "value": 8}, {"source": 609, "target": 221, "value": 8}, {"source": 609, "target": 261, "value": 10}, {"source": 609, "target": 263, "value": 12}, {"source": 609, "target": 309, "value": 2}, {"source": 609, "target": 321, "value": 8}, {"source": 609, "target": 333, "value": 8}, {"source": 609, "target": 351, "value": 10}, {"source": 609, "target": 363, "value": 11}, {"source": 609, "target": 371, "value": 8}, {"source": 609, "target": 405, "value": 8}, {"source": 609, "target": 413, "value": 10}, {"source": 609, "target": 459, "value": 4}, {"source": 609, "target": 461, "value": 4}, {"source": 609, "target": 471, "value": 8}, {"source": 609, "target": 497, "value": 4}, {"source": 609, "target": 513, "value": 10}, {"source": 609, "target": 521, "value": 8}, {"source": 609, "target": 549, "value": 8}, {"source": 609, "target": 563, "value": 9}, {"source": 611, "target": 219, "value": 8}, {"source": 611, "target": 248, "value": 8}, {"source": 611, "target": 249, "value": 9}, {"source": 611, "target": 257, "value": 8}, {"source": 611, "target": 261, "value": 4}, {"source": 611, "target": 273, "value": 9}, {"source": 611, "target": 285, "value": 8}, {"source": 611, "target": 309, "value": 8}, {"source": 611, "target": 311, "value": 4}, {"source": 611, "target": 323, "value": 9}, {"source": 611, "target": 348, "value": 8}, {"source": 611, "target": 363, "value": 10}, {"source": 611, "target": 369, "value": 9}, {"source": 611, "target": 398, "value": 8}, {"source": 611, "target": 411, "value": 4}, {"source": 611, "target": 423, "value": 9}, {"source": 611, "target": 461, "value": 4}, {"source": 611, "target": 473, "value": 8}, {"source": 611, "target": 498, "value": 8}, {"source": 611, "target": 501, "value": 9}, {"source": 611, "target": 519, "value": 8}, {"source": 611, "target": 548, "value": 8}, {"source": 611, "target": 549, "value": 7}, {"source": 611, "target": 557, "value": 8}, {"source": 611, "target": 561, "value": 3}, {"source": 611, "target": 573, "value": 8}, {"source": 615, "target": 215, "value": 4}, {"source": 615, "target": 219, "value": 8}, {"source": 615, "target": 248, "value": 9}, {"source": 615, "target": 257, "value": 9}, {"source": 615, "target": 263, "value": 8}, {"source": 615, "target": 273, "value": 11}, {"source": 615, "target": 309, "value": 10}, {"source": 615, "target": 315, "value": 4}, {"source": 615, "target": 323, "value": 11}, {"source": 615, "target": 348, "value": 9}, {"source": 615, "target": 353, "value": 8}, {"source": 615, "target": 363, "value": 9}, {"source": 615, "target": 365, "value": 4}, {"source": 615, "target": 369, "value": 8}, {"source": 615, "target": 398, "value": 9}, {"source": 615, "target": 405, "value": 10}, {"source": 615, "target": 413, "value": 9}, {"source": 615, "target": 423, "value": 10}, {"source": 615, "target": 453, "value": 4}, {"source": 615, "target": 461, "value": 8}, {"source": 615, "target": 465, "value": 4}, {"source": 615, "target": 473, "value": 10}, {"source": 615, "target": 498, "value": 8}, {"source": 615, "target": 513, "value": 9}, {"source": 615, "target": 515, "value": 4}, {"source": 615, "target": 519, "value": 8}, {"source": 615, "target": 548, "value": 8}, {"source": 615, "target": 557, "value": 8}, {"source": 615, "target": 563, "value": 8}, {"source": 615, "target": 573, "value": 9}, {"source": 615, "target": 605, "value": 8}, {"source": 615, "target": 611, "value": 8}, {"source": 617, "target": 221, "value": 10}, {"source": 617, "target": 233, "value": 11}, {"source": 617, "target": 243, "value": 9}, {"source": 617, "target": 255, "value": 10}, {"source": 617, "target": 279, "value": 8}, {"source": 617, "target": 293, "value": 4}, {"source": 617, "target": 305, "value": 8}, {"source": 617, "target": 317, "value": 2}, {"source": 617, "target": 333, "value": 10}, {"source": 617, "target": 363, "value": 8}, {"source": 617, "target": 383, "value": 10}, {"source": 617, "target": 393, "value": 4}, {"source": 617, "target": 405, "value": 8}, {"source": 617, "target": 429, "value": 8}, {"source": 617, "target": 443, "value": 9}, {"source": 617, "target": 467, "value": 4}, {"source": 617, "target": 483, "value": 9}, {"source": 617, "target": 489, "value": 10}, {"source": 617, "target": 533, "value": 9}, {"source": 617, "target": 543, "value": 8}, {"source": 617, "target": 557, "value": 8}, {"source": 617, "target": 579, "value": 8}, {"source": 617, "target": 593, "value": 4}, {"source": 617, "target": 605, "value": 8}, {"source": 621, "target": 221, "value": 1}, {"source": 621, "target": 231, "value": 8}, {"source": 621, "target": 233, "value": 8}, {"source": 621, "target": 261, "value": 10}, {"source": 621, "target": 273, "value": 3}, {"source": 621, "target": 279, "value": 8}, {"source": 621, "target": 281, "value": 8}, {"source": 621, "target": 293, "value": 12}, {"source": 621, "target": 309, "value": 8}, {"source": 621, "target": 321, "value": 2}, {"source": 621, "target": 323, "value": 9}, {"source": 621, "target": 333, "value": 8}, {"source": 621, "target": 347, "value": 8}, {"source": 621, "target": 369, "value": 8}, {"source": 621, "target": 371, "value": 4}, {"source": 621, "target": 377, "value": 8}, {"source": 621, "target": 381, "value": 8}, {"source": 621, "target": 383, "value": 8}, {"source": 621, "target": 423, "value": 8}, {"source": 621, "target": 431, "value": 8}, {"source": 621, "target": 461, "value": 9}, {"source": 621, "target": 471, "value": 4}, {"source": 621, "target": 473, "value": 4}, {"source": 621, "target": 483, "value": 8}, {"source": 621, "target": 497, "value": 8}, {"source": 621, "target": 519, "value": 8}, {"source": 621, "target": 521, "value": 2}, {"source": 621, "target": 531, "value": 8}, {"source": 621, "target": 533, "value": 8}, {"source": 621, "target": 573, "value": 2}, {"source": 621, "target": 581, "value": 8}, {"source": 621, "target": 609, "value": 8}, {"source": 623, "target": 219, "value": 9}, {"source": 623, "target": 221, "value": 8}, {"source": 623, "target": 248, "value": 8}, {"source": 623, "target": 257, "value": 8}, {"source": 623, "target": 261, "value": 9}, {"source": 623, "target": 273, "value": 1}, {"source": 623, "target": 309, "value": 8}, {"source": 623, "target": 323, "value": 2}, {"source": 623, "target": 347, "value": 8}, {"source": 623, "target": 348, "value": 8}, {"source": 623, "target": 369, "value": 9}, {"source": 623, "target": 398, "value": 8}, {"source": 623, "target": 413, "value": 10}, {"source": 623, "target": 423, "value": 2}, {"source": 623, "target": 461, "value": 8}, {"source": 623, "target": 473, "value": 1}, {"source": 623, "target": 497, "value": 8}, {"source": 623, "target": 498, "value": 8}, {"source": 623, "target": 519, "value": 9}, {"source": 623, "target": 545, "value": 10}, {"source": 623, "target": 548, "value": 8}, {"source": 623, "target": 557, "value": 8}, {"source": 623, "target": 561, "value": 7}, {"source": 623, "target": 573, "value": 1}, {"source": 623, "target": 611, "value": 7}, {"source": 623, "target": 615, "value": 8}, {"source": 623, "target": 621, "value": 7}, {"source": 633, "target": 221, "value": 4}, {"source": 633, "target": 233, "value": 0}, {"source": 633, "target": 245, "value": 8}, {"source": 633, "target": 248, "value": 8}, {"source": 633, "target": 257, "value": 4}, {"source": 633, "target": 279, "value": 4}, {"source": 633, "target": 285, "value": 10}, {"source": 633, "target": 317, "value": 8}, {"source": 633, "target": 321, "value": 4}, {"source": 633, "target": 333, "value": 1}, {"source": 633, "target": 335, "value": 10}, {"source": 633, "target": 345, "value": 8}, {"source": 633, "target": 348, "value": 8}, {"source": 633, "target": 381, "value": 4}, {"source": 633, "target": 383, "value": 1}, {"source": 633, "target": 395, "value": 8}, {"source": 633, "target": 398, "value": 8}, {"source": 633, "target": 405, "value": 11}, {"source": 633, "target": 407, "value": 4}, {"source": 633, "target": 429, "value": 9}, {"source": 633, "target": 483, "value": 1}, {"source": 633, "target": 485, "value": 9}, {"source": 633, "target": 495, "value": 8}, {"source": 633, "target": 498, "value": 8}, {"source": 633, "target": 521, "value": 8}, {"source": 633, "target": 533, "value": 1}, {"source": 633, "target": 545, "value": 8}, {"source": 633, "target": 548, "value": 8}, {"source": 633, "target": 557, "value": 2}, {"source": 633, "target": 573, "value": 8}, {"source": 633, "target": 579, "value": 9}, {"source": 633, "target": 617, "value": 8}, {"source": 633, "target": 621, "value": 8}, {"source": 635, "target": 273, "value": 9}, {"source": 635, "target": 285, "value": 4}, {"source": 635, "target": 335, "value": 4}, {"source": 635, "target": 377, "value": 10}, {"source": 635, "target": 435, "value": 4}, {"source": 635, "target": 473, "value": 8}, {"source": 635, "target": 485, "value": 4}, {"source": 635, "target": 525, "value": 9}, {"source": 635, "target": 531, "value": 8}, {"source": 635, "target": 573, "value": 8}, {"source": 635, "target": 585, "value": 3}, {"source": 635, "target": 587, "value": 9}, {"source": 639, "target": 243, "value": 12}, {"source": 639, "target": 293, "value": 11}, {"source": 639, "target": 335, "value": 8}, {"source": 639, "target": 339, "value": 4}, {"source": 639, "target": 377, "value": 8}, {"source": 639, "target": 393, "value": 11}, {"source": 639, "target": 413, "value": 8}, {"source": 639, "target": 429, "value": 10}, {"source": 639, "target": 443, "value": 10}, {"source": 639, "target": 461, "value": 10}, {"source": 639, "target": 489, "value": 4}, {"source": 639, "target": 543, "value": 10}, {"source": 639, "target": 593, "value": 9}, {"source": 641, "target": 291, "value": 4}, {"source": 641, "target": 293, "value": 5}, {"source": 641, "target": 303, "value": 8}, {"source": 641, "target": 341, "value": 2}, {"source": 641, "target": 353, "value": 8}, {"source": 641, "target": 365, "value": 8}, {"source": 641, "target": 381, "value": 10}, {"source": 641, "target": 429, "value": 8}, {"source": 641, "target": 441, "value": 2}, {"source": 641, "target": 453, "value": 8}, {"source": 641, "target": 491, "value": 4}, {"source": 641, "target": 503, "value": 8}, {"source": 641, "target": 533, "value": 9}, {"source": 641, "target": 581, "value": 8}, {"source": 641, "target": 587, "value": 7}, {"source": 641, "target": 591, "value": 3}, {"source": 641, "target": 603, "value": 8}, {"source": 645, "target": 233, "value": 4}, {"source": 645, "target": 245, "value": 2}, {"source": 645, "target": 248, "value": 8}, {"source": 645, "target": 251, "value": 8}, {"source": 645, "target": 255, "value": 8}, {"source": 645, "target": 257, "value": 8}, {"source": 645, "target": 285, "value": 10}, {"source": 645, "target": 293, "value": 8}, {"source": 645, "target": 305, "value": 8}, {"source": 645, "target": 333, "value": 8}, {"source": 645, "target": 345, "value": 2}, {"source": 645, "target": 348, "value": 8}, {"source": 645, "target": 393, "value": 8}, {"source": 645, "target": 395, "value": 4}, {"source": 645, "target": 398, "value": 8}, {"source": 645, "target": 405, "value": 8}, {"source": 645, "target": 407, "value": 8}, {"source": 645, "target": 455, "value": 8}, {"source": 645, "target": 485, "value": 9}, {"source": 645, "target": 495, "value": 4}, {"source": 645, "target": 498, "value": 8}, {"source": 645, "target": 503, "value": 8}, {"source": 645, "target": 533, "value": 8}, {"source": 645, "target": 545, "value": 2}, {"source": 645, "target": 548, "value": 8}, {"source": 645, "target": 555, "value": 8}, {"source": 645, "target": 557, "value": 8}, {"source": 645, "target": 593, "value": 7}, {"source": 645, "target": 605, "value": 8}, {"source": 645, "target": 633, "value": 8}, {"source": 647, "target": 221, "value": 9}, {"source": 647, "target": 273, "value": 4}, {"source": 647, "target": 285, "value": 9}, {"source": 647, "target": 293, "value": 8}, {"source": 647, "target": 323, "value": 9}, {"source": 647, "target": 347, "value": 4}, {"source": 647, "target": 423, "value": 9}, {"source": 647, "target": 437, "value": 9}, {"source": 647, "target": 473, "value": 4}, {"source": 647, "target": 485, "value": 8}, {"source": 647, "target": 497, "value": 4}, {"source": 647, "target": 573, "value": 4}, {"source": 647, "target": 585, "value": 4}, {"source": 647, "target": 621, "value": 8}, {"source": 647, "target": 623, "value": 8}, {"source": 648, "target": 219, "value": 8}, {"source": 648, "target": 233, "value": 4}, {"source": 648, "target": 245, "value": 8}, {"source": 648, "target": 248, "value": 4}, {"source": 648, "target": 257, "value": 8}, {"source": 648, "target": 273, "value": 11}, {"source": 648, "target": 285, "value": 10}, {"source": 648, "target": 309, "value": 9}, {"source": 648, "target": 323, "value": 10}, {"source": 648, "target": 333, "value": 9}, {"source": 648, "target": 345, "value": 8}, {"source": 648, "target": 348, "value": 4}, {"source": 648, "target": 369, "value": 8}, {"source": 648, "target": 395, "value": 8}, {"source": 648, "target": 398, "value": 4}, {"source": 648, "target": 423, "value": 10}, {"source": 648, "target": 473, "value": 9}, {"source": 648, "target": 485, "value": 9}, {"source": 648, "target": 495, "value": 8}, {"source": 648, "target": 498, "value": 4}, {"source": 648, "target": 519, "value": 8}, {"source": 648, "target": 533, "value": 8}, {"source": 648, "target": 545, "value": 8}, {"source": 648, "target": 548, "value": 4}, {"source": 648, "target": 557, "value": 7}, {"source": 648, "target": 573, "value": 8}, {"source": 648, "target": 611, "value": 8}, {"source": 648, "target": 615, "value": 8}, {"source": 648, "target": 623, "value": 8}, {"source": 648, "target": 633, "value": 8}, {"source": 648, "target": 645, "value": 8}, {"source": 651, "target": 251, "value": 4}, {"source": 651, "target": 263, "value": 8}, {"source": 651, "target": 341, "value": 10}, {"source": 651, "target": 351, "value": 4}, {"source": 651, "target": 363, "value": 8}, {"source": 651, "target": 401, "value": 2}, {"source": 651, "target": 413, "value": 8}, {"source": 651, "target": 453, "value": 8}, {"source": 651, "target": 455, "value": 9}, {"source": 651, "target": 489, "value": 8}, {"source": 651, "target": 501, "value": 2}, {"source": 651, "target": 503, "value": 9}, {"source": 651, "target": 513, "value": 8}, {"source": 651, "target": 525, "value": 8}, {"source": 651, "target": 551, "value": 4}, {"source": 651, "target": 563, "value": 8}, {"source": 651, "target": 573, "value": 9}, {"source": 653, "target": 215, "value": 8}, {"source": 653, "target": 227, "value": 4}, {"source": 653, "target": 228, "value": 8}, {"source": 653, "target": 249, "value": 8}, {"source": 653, "target": 263, "value": 8}, {"source": 653, "target": 278, "value": 8}, {"source": 653, "target": 279, "value": 3}, {"source": 653, "target": 293, "value": 10}, {"source": 653, "target": 303, "value": 1}, {"source": 653, "target": 315, "value": 8}, {"source": 653, "target": 341, "value": 8}, {"source": 653, "target": 353, "value": 1}, {"source": 653, "target": 363, "value": 8}, {"source": 653, "target": 365, "value": 8}, {"source": 653, "target": 377, "value": 4}, {"source": 653, "target": 378, "value": 8}, {"source": 653, "target": 399, "value": 9}, {"source": 653, "target": 401, "value": 8}, {"source": 653, "target": 405, "value": 4}, {"source": 653, "target": 413, "value": 8}, {"source": 653, "target": 428, "value": 8}, {"source": 653, "target": 429, "value": 8}, {"source": 653, "target": 437, "value": 8}, {"source": 653, "target": 441, "value": 8}, {"source": 653, "target": 453, "value": 0}, {"source": 653, "target": 455, "value": 8}, {"source": 653, "target": 461, "value": 8}, {"source": 653, "target": 465, "value": 8}, {"source": 653, "target": 501, "value": 8}, {"source": 653, "target": 503, "value": 1}, {"source": 653, "target": 513, "value": 8}, {"source": 653, "target": 515, "value": 8}, {"source": 653, "target": 527, "value": 4}, {"source": 653, "target": 528, "value": 8}, {"source": 653, "target": 531, "value": 8}, {"source": 653, "target": 545, "value": 9}, {"source": 653, "target": 549, "value": 9}, {"source": 653, "target": 563, "value": 8}, {"source": 653, "target": 578, "value": 8}, {"source": 653, "target": 579, "value": 8}, {"source": 653, "target": 603, "value": 1}, {"source": 653, "target": 605, "value": 8}, {"source": 653, "target": 615, "value": 8}, {"source": 653, "target": 641, "value": 8}, {"source": 653, "target": 651, "value": 7}, {"source": 663, "target": 221, "value": 8}, {"source": 663, "target": 261, "value": 9}, {"source": 663, "target": 263, "value": 2}, {"source": 663, "target": 287, "value": 8}, {"source": 663, "target": 309, "value": 4}, {"source": 663, "target": 321, "value": 8}, {"source": 663, "target": 351, "value": 9}, {"source": 663, "target": 363, "value": 2}, {"source": 663, "target": 371, "value": 8}, {"source": 663, "target": 401, "value": 8}, {"source": 663, "target": 405, "value": 8}, {"source": 663, "target": 413, "value": 1}, {"source": 663, "target": 437, "value": 8}, {"source": 663, "target": 453, "value": 9}, {"source": 663, "target": 455, "value": 10}, {"source": 663, "target": 459, "value": 9}, {"source": 663, "target": 461, "value": 4}, {"source": 663, "target": 471, "value": 8}, {"source": 663, "target": 489, "value": 8}, {"source": 663, "target": 497, "value": 4}, {"source": 663, "target": 501, "value": 8}, {"source": 663, "target": 513, "value": 1}, {"source": 663, "target": 521, "value": 9}, {"source": 663, "target": 549, "value": 8}, {"source": 663, "target": 563, "value": 2}, {"source": 663, "target": 587, "value": 8}, {"source": 663, "target": 609, "value": 4}, {"source": 663, "target": 615, "value": 8}, {"source": 663, "target": 621, "value": 8}, {"source": 663, "target": 651, "value": 8}, {"source": 663, "target": 653, "value": 8}, {"source": 665, "target": 215, "value": 4}, {"source": 665, "target": 227, "value": 8}, {"source": 665, "target": 315, "value": 4}, {"source": 665, "target": 317, "value": 10}, {"source": 665, "target": 353, "value": 8}, {"source": 665, "target": 365, "value": 2}, {"source": 665, "target": 377, "value": 8}, {"source": 665, "target": 405, "value": 10}, {"source": 665, "target": 453, "value": 4}, {"source": 665, "target": 461, "value": 8}, {"source": 665, "target": 465, "value": 2}, {"source": 665, "target": 515, "value": 4}, {"source": 665, "target": 527, "value": 8}, {"source": 665, "target": 573, "value": 7}, {"source": 665, "target": 605, "value": 8}, {"source": 665, "target": 615, "value": 3}, {"source": 665, "target": 653, "value": 8}, {"source": 669, "target": 219, "value": 4}, {"source": 669, "target": 221, "value": 5}, {"source": 669, "target": 231, "value": 8}, {"source": 669, "target": 233, "value": 10}, {"source": 669, "target": 245, "value": 8}, {"source": 669, "target": 248, "value": 9}, {"source": 669, "target": 257, "value": 9}, {"source": 669, "target": 273, "value": 5}, {"source": 669, "target": 279, "value": 8}, {"source": 669, "target": 281, "value": 8}, {"source": 669, "target": 309, "value": 10}, {"source": 669, "target": 317, "value": 8}, {"source": 669, "target": 321, "value": 10}, {"source": 669, "target": 323, "value": 11}, {"source": 669, "target": 333, "value": 10}, {"source": 669, "target": 345, "value": 8}, {"source": 669, "target": 348, "value": 9}, {"source": 669, "target": 369, "value": 2}, {"source": 669, "target": 377, "value": 8}, {"source": 669, "target": 381, "value": 8}, {"source": 669, "target": 383, "value": 9}, {"source": 669, "target": 395, "value": 8}, {"source": 669, "target": 398, "value": 9}, {"source": 669, "target": 423, "value": 10}, {"source": 669, "target": 429, "value": 8}, {"source": 669, "target": 431, "value": 8}, {"source": 669, "target": 473, "value": 10}, {"source": 669, "target": 483, "value": 9}, {"source": 669, "target": 495, "value": 8}, {"source": 669, "target": 498, "value": 8}, {"source": 669, "target": 519, "value": 2}, {"source": 669, "target": 531, "value": 8}, {"source": 669, "target": 533, "value": 8}, {"source": 669, "target": 545, "value": 8}, {"source": 669, "target": 548, "value": 8}, {"source": 669, "target": 557, "value": 8}, {"source": 669, "target": 573, "value": 9}, {"source": 669, "target": 579, "value": 8}, {"source": 669, "target": 581, "value": 8}, {"source": 669, "target": 611, "value": 9}, {"source": 669, "target": 615, "value": 7}, {"source": 669, "target": 617, "value": 7}, {"source": 669, "target": 621, "value": 8}, {"source": 669, "target": 623, "value": 9}, {"source": 669, "target": 633, "value": 8}, {"source": 669, "target": 645, "value": 8}, {"source": 669, "target": 648, "value": 7}, {"source": 671, "target": 219, "value": 8}, {"source": 671, "target": 221, "value": 4}, {"source": 671, "target": 225, "value": 8}, {"source": 671, "target": 228, "value": 8}, {"source": 671, "target": 261, "value": 10}, {"source": 671, "target": 275, "value": 8}, {"source": 671, "target": 278, "value": 8}, {"source": 671, "target": 293, "value": 12}, {"source": 671, "target": 309, "value": 8}, {"source": 671, "target": 321, "value": 2}, {"source": 671, "target": 365, "value": 10}, {"source": 671, "target": 369, "value": 8}, {"source": 671, "target": 371, "value": 4}, {"source": 671, "target": 375, "value": 8}, {"source": 671, "target": 378, "value": 8}, {"source": 671, "target": 413, "value": 8}, {"source": 671, "target": 425, "value": 8}, {"source": 671, "target": 428, "value": 8}, {"source": 671, "target": 461, "value": 9}, {"source": 671, "target": 471, "value": 4}, {"source": 671, "target": 513, "value": 8}, {"source": 671, "target": 519, "value": 8}, {"source": 671, "target": 521, "value": 4}, {"source": 671, "target": 525, "value": 8}, {"source": 671, "target": 528, "value": 8}, {"source": 671, "target": 563, "value": 9}, {"source": 671, "target": 573, "value": 9}, {"source": 671, "target": 575, "value": 7}, {"source": 671, "target": 578, "value": 8}, {"source": 671, "target": 609, "value": 7}, {"source": 671, "target": 621, "value": 3}, {"source": 671, "target": 663, "value": 8}, {"source": 671, "target": 669, "value": 7}, {"source": 675, "target": 225, "value": 4}, {"source": 675, "target": 228, "value": 8}, {"source": 675, "target": 275, "value": 4}, {"source": 675, "target": 278, "value": 8}, {"source": 675, "target": 321, "value": 8}, {"source": 675, "target": 365, "value": 10}, {"source": 675, "target": 375, "value": 4}, {"source": 675, "target": 377, "value": 10}, {"source": 675, "target": 378, "value": 8}, {"source": 675, "target": 413, "value": 8}, {"source": 675, "target": 425, "value": 4}, {"source": 675, "target": 428, "value": 8}, {"source": 675, "target": 513, "value": 8}, {"source": 675, "target": 525, "value": 4}, {"source": 675, "target": 528, "value": 8}, {"source": 675, "target": 563, "value": 8}, {"source": 675, "target": 575, "value": 3}, {"source": 675, "target": 578, "value": 8}, {"source": 675, "target": 671, "value": 7}, {"source": 677, "target": 225, "value": 8}, {"source": 677, "target": 227, "value": 4}, {"source": 677, "target": 243, "value": 11}, {"source": 677, "target": 279, "value": 10}, {"source": 677, "target": 287, "value": 8}, {"source": 677, "target": 293, "value": 11}, {"source": 677, "target": 303, "value": 9}, {"source": 677, "target": 317, "value": 10}, {"source": 677, "target": 335, "value": 8}, {"source": 677, "target": 339, "value": 8}, {"source": 677, "target": 353, "value": 4}, {"source": 677, "target": 365, "value": 4}, {"source": 677, "target": 377, "value": 2}, {"source": 677, "target": 393, "value": 10}, {"source": 677, "target": 425, "value": 8}, {"source": 677, "target": 429, "value": 4}, {"source": 677, "target": 437, "value": 8}, {"source": 677, "target": 443, "value": 10}, {"source": 677, "target": 453, "value": 4}, {"source": 677, "target": 465, "value": 8}, {"source": 677, "target": 489, "value": 8}, {"source": 677, "target": 503, "value": 9}, {"source": 677, "target": 525, "value": 8}, {"source": 677, "target": 527, "value": 4}, {"source": 677, "target": 543, "value": 9}, {"source": 677, "target": 573, "value": 8}, {"source": 677, "target": 587, "value": 8}, {"source": 677, "target": 593, "value": 9}, {"source": 677, "target": 603, "value": 8}, {"source": 677, "target": 639, "value": 8}, {"source": 677, "target": 653, "value": 4}, {"source": 677, "target": 665, "value": 8}, {"source": 678, "target": 225, "value": 8}, {"source": 678, "target": 228, "value": 4}, {"source": 678, "target": 249, "value": 8}, {"source": 678, "target": 261, "value": 8}, {"source": 678, "target": 273, "value": 8}, {"source": 678, "target": 275, "value": 8}, {"source": 678, "target": 278, "value": 4}, {"source": 678, "target": 303, "value": 10}, {"source": 678, "target": 321, "value": 8}, {"source": 678, "target": 323, "value": 8}, {"source": 678, "target": 353, "value": 10}, {"source": 678, "target": 365, "value": 10}, {"source": 678, "target": 375, "value": 8}, {"source": 678, "target": 378, "value": 4}, {"source": 678, "target": 399, "value": 8}, {"source": 678, "target": 413, "value": 4}, {"source": 678, "target": 423, "value": 8}, {"source": 678, "target": 425, "value": 8}, {"source": 678, "target": 428, "value": 4}, {"source": 678, "target": 437, "value": 8}, {"source": 678, "target": 453, "value": 9}, {"source": 678, "target": 461, "value": 8}, {"source": 678, "target": 473, "value": 8}, {"source": 678, "target": 503, "value": 9}, {"source": 678, "target": 513, "value": 8}, {"source": 678, "target": 525, "value": 8}, {"source": 678, "target": 528, "value": 4}, {"source": 678, "target": 545, "value": 8}, {"source": 678, "target": 549, "value": 8}, {"source": 678, "target": 561, "value": 8}, {"source": 678, "target": 563, "value": 9}, {"source": 678, "target": 573, "value": 8}, {"source": 678, "target": 575, "value": 8}, {"source": 678, "target": 578, "value": 4}, {"source": 678, "target": 603, "value": 8}, {"source": 678, "target": 623, "value": 8}, {"source": 678, "target": 653, "value": 8}, {"source": 678, "target": 671, "value": 7}, {"source": 678, "target": 675, "value": 8}, {"source": 681, "target": 221, "value": 11}, {"source": 681, "target": 231, "value": 4}, {"source": 681, "target": 243, "value": 8}, {"source": 681, "target": 273, "value": 11}, {"source": 681, "target": 281, "value": 2}, {"source": 681, "target": 293, "value": 8}, {"source": 681, "target": 333, "value": 10}, {"source": 681, "target": 369, "value": 8}, {"source": 681, "target": 377, "value": 8}, {"source": 681, "target": 381, "value": 2}, {"source": 681, "target": 393, "value": 8}, {"source": 681, "target": 398, "value": 9}, {"source": 681, "target": 431, "value": 2}, {"source": 681, "target": 443, "value": 8}, {"source": 681, "target": 489, "value": 8}, {"source": 681, "target": 519, "value": 9}, {"source": 681, "target": 531, "value": 4}, {"source": 681, "target": 533, "value": 9}, {"source": 681, "target": 543, "value": 8}, {"source": 681, "target": 581, "value": 2}, {"source": 681, "target": 585, "value": 9}, {"source": 681, "target": 593, "value": 8}, {"source": 681, "target": 605, "value": 8}, {"source": 681, "target": 621, "value": 8}, {"source": 681, "target": 669, "value": 8}, {"source": 683, "target": 221, "value": 4}, {"source": 683, "target": 233, "value": 1}, {"source": 683, "target": 257, "value": 8}, {"source": 683, "target": 279, "value": 4}, {"source": 683, "target": 317, "value": 8}, {"source": 683, "target": 321, "value": 4}, {"source": 683, "target": 333, "value": 1}, {"source": 683, "target": 335, "value": 10}, {"source": 683, "target": 381, "value": 8}, {"source": 683, "target": 383, "value": 2}, {"source": 683, "target": 405, "value": 11}, {"source": 683, "target": 407, "value": 8}, {"source": 683, "target": 429, "value": 9}, {"source": 683, "target": 483, "value": 2}, {"source": 683, "target": 521, "value": 8}, {"source": 683, "target": 533, "value": 1}, {"source": 683, "target": 557, "value": 4}, {"source": 683, "target": 573, "value": 9}, {"source": 683, "target": 579, "value": 9}, {"source": 683, "target": 617, "value": 8}, {"source": 683, "target": 621, "value": 7}, {"source": 683, "target": 633, "value": 1}, {"source": 683, "target": 669, "value": 8}, {"source": 693, "target": 243, "value": 1}, {"source": 693, "target": 245, "value": 10}, {"source": 693, "target": 251, "value": 8}, {"source": 693, "target": 255, "value": 8}, {"source": 693, "target": 281, "value": 9}, {"source": 693, "target": 293, "value": 0}, {"source": 693, "target": 303, "value": 8}, {"source": 693, "target": 305, "value": 8}, {"source": 693, "target": 317, "value": 2}, {"source": 693, "target": 333, "value": 10}, {"source": 693, "target": 335, "value": 8}, {"source": 693, "target": 339, "value": 8}, {"source": 693, "target": 341, "value": 8}, {"source": 693, "target": 353, "value": 8}, {"source": 693, "target": 377, "value": 8}, {"source": 693, "target": 381, "value": 8}, {"source": 693, "target": 393, "value": 1}, {"source": 693, "target": 398, "value": 9}, {"source": 693, "target": 405, "value": 8}, {"source": 693, "target": 429, "value": 8}, {"source": 693, "target": 431, "value": 9}, {"source": 693, "target": 441, "value": 8}, {"source": 693, "target": 443, "value": 1}, {"source": 693, "target": 453, "value": 8}, {"source": 693, "target": 455, "value": 8}, {"source": 693, "target": 467, "value": 4}, {"source": 693, "target": 489, "value": 2}, {"source": 693, "target": 503, "value": 8}, {"source": 693, "target": 533, "value": 9}, {"source": 693, "target": 543, "value": 1}, {"source": 693, "target": 545, "value": 9}, {"source": 693, "target": 555, "value": 8}, {"source": 693, "target": 581, "value": 8}, {"source": 693, "target": 585, "value": 9}, {"source": 693, "target": 593, "value": 1}, {"source": 693, "target": 603, "value": 8}, {"source": 693, "target": 605, "value": 8}, {"source": 693, "target": 617, "value": 4}, {"source": 693, "target": 639, "value": 8}, {"source": 693, "target": 641, "value": 7}, {"source": 693, "target": 645, "value": 8}, {"source": 693, "target": 653, "value": 8}, {"source": 693, "target": 677, "value": 8}, {"source": 693, "target": 681, "value": 8}, {"source": 695, "target": 233, "value": 4}, {"source": 695, "target": 245, "value": 2}, {"source": 695, "target": 248, "value": 8}, {"source": 695, "target": 257, "value": 8}, {"source": 695, "target": 285, "value": 10}, {"source": 695, "target": 333, "value": 8}, {"source": 695, "target": 345, "value": 2}, {"source": 695, "target": 348, "value": 8}, {"source": 695, "target": 395, "value": 4}, {"source": 695, "target": 398, "value": 8}, {"source": 695, "target": 407, "value": 8}, {"source": 695, "target": 485, "value": 9}, {"source": 695, "target": 495, "value": 4}, {"source": 695, "target": 498, "value": 8}, {"source": 695, "target": 503, "value": 8}, {"source": 695, "target": 533, "value": 8}, {"source": 695, "target": 545, "value": 2}, {"source": 695, "target": 548, "value": 8}, {"source": 695, "target": 557, "value": 8}, {"source": 695, "target": 633, "value": 7}, {"source": 695, "target": 645, "value": 2}, {"source": 695, "target": 648, "value": 8}, {"source": 695, "target": 669, "value": 8}, {"source": 698, "target": 219, "value": 8}, {"source": 698, "target": 233, "value": 4}, {"source": 698, "target": 245, "value": 8}, {"source": 698, "target": 248, "value": 4}, {"source": 698, "target": 257, "value": 8}, {"source": 698, "target": 273, "value": 11}, {"source": 698, "target": 285, "value": 11}, {"source": 698, "target": 309, "value": 10}, {"source": 698, "target": 323, "value": 10}, {"source": 698, "target": 333, "value": 9}, {"source": 698, "target": 345, "value": 8}, {"source": 698, "target": 348, "value": 4}, {"source": 698, "target": 369, "value": 8}, {"source": 698, "target": 395, "value": 8}, {"source": 698, "target": 398, "value": 4}, {"source": 698, "target": 423, "value": 10}, {"source": 698, "target": 473, "value": 9}, {"source": 698, "target": 485, "value": 10}, {"source": 698, "target": 495, "value": 8}, {"source": 698, "target": 498, "value": 4}, {"source": 698, "target": 519, "value": 8}, {"source": 698, "target": 533, "value": 8}, {"source": 698, "target": 545, "value": 8}, {"source": 698, "target": 548, "value": 4}, {"source": 698, "target": 557, "value": 8}, {"source": 698, "target": 573, "value": 9}, {"source": 698, "target": 611, "value": 8}, {"source": 698, "target": 615, "value": 8}, {"source": 698, "target": 623, "value": 8}, {"source": 698, "target": 633, "value": 8}, {"source": 698, "target": 645, "value": 7}, {"source": 698, "target": 648, "value": 3}, {"source": 698, "target": 669, "value": 8}, {"source": 698, "target": 695, "value": 8}, {"source": 699, "target": 221, "value": 8}, {"source": 699, "target": 228, "value": 9}, {"source": 699, "target": 249, "value": 4}, {"source": 699, "target": 251, "value": 10}, {"source": 699, "target": 273, "value": 4}, {"source": 699, "target": 278, "value": 9}, {"source": 699, "target": 293, "value": 10}, {"source": 699, "target": 303, "value": 4}, {"source": 699, "target": 321, "value": 11}, {"source": 699, "target": 323, "value": 8}, {"source": 699, "target": 341, "value": 8}, {"source": 699, "target": 347, "value": 8}, {"source": 699, "target": 353, "value": 4}, {"source": 699, "target": 378, "value": 9}, {"source": 699, "target": 399, "value": 4}, {"source": 699, "target": 423, "value": 8}, {"source": 699, "target": 428, "value": 9}, {"source": 699, "target": 437, "value": 8}, {"source": 699, "target": 441, "value": 8}, {"source": 699, "target": 453, "value": 4}, {"source": 699, "target": 473, "value": 4}, {"source": 699, "target": 497, "value": 8}, {"source": 699, "target": 503, "value": 4}, {"source": 699, "target": 528, "value": 8}, {"source": 699, "target": 545, "value": 8}, {"source": 699, "target": 549, "value": 4}, {"source": 699, "target": 573, "value": 2}, {"source": 699, "target": 578, "value": 8}, {"source": 699, "target": 603, "value": 4}, {"source": 699, "target": 621, "value": 8}, {"source": 699, "target": 623, "value": 8}, {"source": 699, "target": 641, "value": 8}, {"source": 699, "target": 647, "value": 8}, {"source": 699, "target": 653, "value": 4}, {"source": 699, "target": 678, "value": 7}, {"source": 699, "target": 693, "value": 8}, {"source": 701, "target": 227, "value": 8}, {"source": 701, "target": 249, "value": 8}, {"source": 701, "target": 251, "value": 4}, {"source": 701, "target": 261, "value": 8}, {"source": 701, "target": 263, "value": 8}, {"source": 701, "target": 279, "value": 10}, {"source": 701, "target": 303, "value": 9}, {"source": 701, "target": 311, "value": 8}, {"source": 701, "target": 341, "value": 10}, {"source": 701, "target": 351, "value": 4}, {"source": 701, "target": 353, "value": 4}, {"source": 701, "target": 363, "value": 8}, {"source": 701, "target": 377, "value": 8}, {"source": 701, "target": 401, "value": 2}, {"source": 701, "target": 411, "value": 8}, {"source": 701, "target": 413, "value": 8}, {"source": 701, "target": 453, "value": 3}, {"source": 701, "target": 455, "value": 9}, {"source": 701, "target": 461, "value": 8}, {"source": 701, "target": 489, "value": 8}, {"source": 701, "target": 501, "value": 2}, {"source": 701, "target": 503, "value": 4}, {"source": 701, "target": 513, "value": 8}, {"source": 701, "target": 525, "value": 8}, {"source": 701, "target": 527, "value": 8}, {"source": 701, "target": 549, "value": 8}, {"source": 701, "target": 551, "value": 4}, {"source": 701, "target": 561, "value": 8}, {"source": 701, "target": 563, "value": 8}, {"source": 701, "target": 573, "value": 10}, {"source": 701, "target": 603, "value": 8}, {"source": 701, "target": 611, "value": 8}, {"source": 701, "target": 651, "value": 2}, {"source": 701, "target": 653, "value": 2}, {"source": 701, "target": 663, "value": 8}, {"source": 701, "target": 677, "value": 8}, {"source": 705, "target": 245, "value": 11}, {"source": 705, "target": 251, "value": 8}, {"source": 705, "target": 255, "value": 2}, {"source": 705, "target": 293, "value": 9}, {"source": 705, "target": 305, "value": 2}, {"source": 705, "target": 317, "value": 8}, {"source": 705, "target": 363, "value": 8}, {"source": 705, "target": 377, "value": 11}, {"source": 705, "target": 393, "value": 8}, {"source": 705, "target": 405, "value": 2}, {"source": 705, "target": 429, "value": 8}, {"source": 705, "target": 455, "value": 4}, {"source": 705, "target": 467, "value": 8}, {"source": 705, "target": 555, "value": 4}, {"source": 705, "target": 557, "value": 9}, {"source": 705, "target": 593, "value": 8}, {"source": 705, "target": 605, "value": 2}, {"source": 705, "target": 617, "value": 8}, {"source": 705, "target": 645, "value": 8}, {"source": 705, "target": 693, "value": 8}, {"source": 707, "target": 233, "value": 4}, {"source": 707, "target": 245, "value": 9}, {"source": 707, "target": 257, "value": 4}, {"source": 707, "target": 333, "value": 4}, {"source": 707, "target": 345, "value": 8}, {"source": 707, "target": 381, "value": 8}, {"source": 707, "target": 383, "value": 9}, {"source": 707, "target": 407, "value": 4}, {"source": 707, "target": 483, "value": 9}, {"source": 707, "target": 503, "value": 8}, {"source": 707, "target": 533, "value": 4}, {"source": 707, "target": 545, "value": 8}, {"source": 707, "target": 557, "value": 4}, {"source": 707, "target": 633, "value": 4}, {"source": 707, "target": 645, "value": 7}, {"source": 707, "target": 683, "value": 8}, {"source": 707, "target": 695, "value": 8}, {"source": 711, "target": 249, "value": 9}, {"source": 711, "target": 261, "value": 4}, {"source": 711, "target": 285, "value": 8}, {"source": 711, "target": 311, "value": 4}, {"source": 711, "target": 363, "value": 10}, {"source": 711, "target": 411, "value": 4}, {"source": 711, "target": 461, "value": 4}, {"source": 711, "target": 501, "value": 9}, {"source": 711, "target": 549, "value": 8}, {"source": 711, "target": 561, "value": 4}, {"source": 711, "target": 611, "value": 4}, {"source": 711, "target": 701, "value": 8}, {"source": 713, "target": 225, "value": 8}, {"source": 713, "target": 228, "value": 8}, {"source": 713, "target": 255, "value": 10}, {"source": 713, "target": 261, "value": 8}, {"source": 713, "target": 263, "value": 1}, {"source": 713, "target": 275, "value": 8}, {"source": 713, "target": 278, "value": 8}, {"source": 713, "target": 285, "value": 8}, {"source": 713, "target": 287, "value": 4}, {"source": 713, "target": 291, "value": 9}, {"source": 713, "target": 293, "value": 8}, {"source": 713, "target": 305, "value": 8}, {"source": 713, "target": 309, "value": 8}, {"source": 713, "target": 311, "value": 8}, {"source": 713, "target": 317, "value": 8}, {"source": 713, "target": 321, "value": 8}, {"source": 713, "target": 341, "value": 9}, {"source": 713, "target": 351, "value": 9}, {"source": 713, "target": 363, "value": 1}, {"source": 713, "target": 365, "value": 4}, {"source": 713, "target": 375, "value": 8}, {"source": 713, "target": 378, "value": 8}, {"source": 713, "target": 401, "value": 8}, {"source": 713, "target": 405, "value": 4}, {"source": 713, "target": 411, "value": 8}, {"source": 713, "target": 413, "value": 1}, {"source": 713, "target": 425, "value": 8}, {"source": 713, "target": 428, "value": 8}, {"source": 713, "target": 437, "value": 4}, {"source": 713, "target": 441, "value": 9}, {"source": 713, "target": 453, "value": 9}, {"source": 713, "target": 455, "value": 10}, {"source": 713, "target": 459, "value": 8}, {"source": 713, "target": 461, "value": 2}, {"source": 713, "target": 467, "value": 8}, {"source": 713, "target": 489, "value": 4}, {"source": 713, "target": 491, "value": 9}, {"source": 713, "target": 497, "value": 4}, {"source": 713, "target": 501, "value": 8}, {"source": 713, "target": 513, "value": 1}, {"source": 713, "target": 525, "value": 8}, {"source": 713, "target": 528, "value": 8}, {"source": 713, "target": 549, "value": 8}, {"source": 713, "target": 557, "value": 9}, {"source": 713, "target": 561, "value": 8}, {"source": 713, "target": 563, "value": 1}, {"source": 713, "target": 575, "value": 8}, {"source": 713, "target": 578, "value": 8}, {"source": 713, "target": 587, "value": 4}, {"source": 713, "target": 591, "value": 9}, {"source": 713, "target": 605, "value": 8}, {"source": 713, "target": 609, "value": 9}, {"source": 713, "target": 611, "value": 8}, {"source": 713, "target": 615, "value": 9}, {"source": 713, "target": 617, "value": 8}, {"source": 713, "target": 641, "value": 9}, {"source": 713, "target": 651, "value": 8}, {"source": 713, "target": 653, "value": 8}, {"source": 713, "target": 663, "value": 1}, {"source": 713, "target": 671, "value": 7}, {"source": 713, "target": 675, "value": 8}, {"source": 713, "target": 678, "value": 8}, {"source": 713, "target": 701, "value": 8}, {"source": 713, "target": 705, "value": 7}, {"source": 713, "target": 711, "value": 7}, {"source": 723, "target": 219, "value": 8}, {"source": 723, "target": 221, "value": 8}, {"source": 723, "target": 248, "value": 8}, {"source": 723, "target": 257, "value": 8}, {"source": 723, "target": 261, "value": 9}, {"source": 723, "target": 273, "value": 1}, {"source": 723, "target": 309, "value": 9}, {"source": 723, "target": 323, "value": 2}, {"source": 723, "target": 347, "value": 8}, {"source": 723, "target": 348, "value": 8}, {"source": 723, "target": 369, "value": 8}, {"source": 723, "target": 398, "value": 8}, {"source": 723, "target": 413, "value": 10}, {"source": 723, "target": 423, "value": 2}, {"source": 723, "target": 461, "value": 8}, {"source": 723, "target": 473, "value": 1}, {"source": 723, "target": 497, "value": 8}, {"source": 723, "target": 498, "value": 8}, {"source": 723, "target": 519, "value": 8}, {"source": 723, "target": 545, "value": 10}, {"source": 723, "target": 548, "value": 8}, {"source": 723, "target": 557, "value": 8}, {"source": 723, "target": 561, "value": 8}, {"source": 723, "target": 573, "value": 1}, {"source": 723, "target": 611, "value": 8}, {"source": 723, "target": 615, "value": 8}, {"source": 723, "target": 621, "value": 8}, {"source": 723, "target": 623, "value": 2}, {"source": 723, "target": 647, "value": 8}, {"source": 723, "target": 648, "value": 8}, {"source": 723, "target": 669, "value": 8}, {"source": 723, "target": 678, "value": 8}, {"source": 723, "target": 698, "value": 8}, {"source": 723, "target": 699, "value": 8}, {"source": 725, "target": 225, "value": 2}, {"source": 725, "target": 228, "value": 8}, {"source": 725, "target": 273, "value": 8}, {"source": 725, "target": 275, "value": 4}, {"source": 725, "target": 278, "value": 8}, {"source": 725, "target": 285, "value": 8}, {"source": 725, "target": 287, "value": 8}, {"source": 725, "target": 321, "value": 8}, {"source": 725, "target": 335, "value": 8}, {"source": 725, "target": 365, "value": 10}, {"source": 725, "target": 375, "value": 4}, {"source": 725, "target": 377, "value": 4}, {"source": 725, "target": 378, "value": 8}, {"source": 725, "target": 413, "value": 8}, {"source": 725, "target": 425, "value": 2}, {"source": 725, "target": 428, "value": 8}, {"source": 725, "target": 429, "value": 10}, {"source": 725, "target": 435, "value": 8}, {"source": 725, "target": 437, "value": 8}, {"source": 725, "target": 473, "value": 8}, {"source": 725, "target": 485, "value": 8}, {"source": 725, "target": 513, "value": 8}, {"source": 725, "target": 525, "value": 2}, {"source": 725, "target": 528, "value": 8}, {"source": 725, "target": 531, "value": 8}, {"source": 725, "target": 563, "value": 9}, {"source": 725, "target": 573, "value": 8}, {"source": 725, "target": 575, "value": 4}, {"source": 725, "target": 578, "value": 8}, {"source": 725, "target": 585, "value": 8}, {"source": 725, "target": 587, "value": 8}, {"source": 725, "target": 635, "value": 8}, {"source": 725, "target": 671, "value": 7}, {"source": 725, "target": 675, "value": 3}, {"source": 725, "target": 677, "value": 8}, {"source": 725, "target": 678, "value": 7}, {"source": 725, "target": 713, "value": 8}, {"source": 728, "target": 225, "value": 8}, {"source": 728, "target": 228, "value": 4}, {"source": 728, "target": 249, "value": 8}, {"source": 728, "target": 275, "value": 8}, {"source": 728, "target": 278, "value": 4}, {"source": 728, "target": 303, "value": 11}, {"source": 728, "target": 321, "value": 8}, {"source": 728, "target": 353, "value": 10}, {"source": 728, "target": 365, "value": 10}, {"source": 728, "target": 375, "value": 8}, {"source": 728, "target": 378, "value": 4}, {"source": 728, "target": 399, "value": 8}, {"source": 728, "target": 413, "value": 8}, {"source": 728, "target": 425, "value": 8}, {"source": 728, "target": 428, "value": 4}, {"source": 728, "target": 437, "value": 8}, {"source": 728, "target": 453, "value": 10}, {"source": 728, "target": 503, "value": 9}, {"source": 728, "target": 513, "value": 8}, {"source": 728, "target": 525, "value": 8}, {"source": 728, "target": 528, "value": 4}, {"source": 728, "target": 545, "value": 8}, {"source": 728, "target": 549, "value": 8}, {"source": 728, "target": 563, "value": 9}, {"source": 728, "target": 575, "value": 8}, {"source": 728, "target": 578, "value": 4}, {"source": 728, "target": 603, "value": 9}, {"source": 728, "target": 653, "value": 8}, {"source": 728, "target": 671, "value": 8}, {"source": 728, "target": 675, "value": 7}, {"source": 728, "target": 678, "value": 3}, {"source": 728, "target": 699, "value": 8}, {"source": 728, "target": 713, "value": 8}, {"source": 728, "target": 725, "value": 8}, {"source": 729, "target": 221, "value": 11}, {"source": 729, "target": 233, "value": 12}, {"source": 729, "target": 279, "value": 4}, {"source": 729, "target": 291, "value": 8}, {"source": 729, "target": 317, "value": 9}, {"source": 729, "target": 333, "value": 11}, {"source": 729, "target": 341, "value": 8}, {"source": 729, "target": 381, "value": 9}, {"source": 729, "target": 383, "value": 11}, {"source": 729, "target": 429, "value": 2}, {"source": 729, "target": 441, "value": 8}, {"source": 729, "target": 483, "value": 10}, {"source": 729, "target": 491, "value": 8}, {"source": 729, "target": 531, "value": 9}, {"source": 729, "target": 533, "value": 4}, {"source": 729, "target": 579, "value": 4}, {"source": 729, "target": 581, "value": 9}, {"source": 729, "target": 587, "value": 8}, {"source": 729, "target": 591, "value": 8}, {"source": 729, "target": 617, "value": 8}, {"source": 729, "target": 633, "value": 9}, {"source": 729, "target": 641, "value": 8}, {"source": 729, "target": 653, "value": 8}, {"source": 729, "target": 669, "value": 8}, {"source": 729, "target": 683, "value": 9}, {"source": 731, "target": 221, "value": 11}, {"source": 731, "target": 231, "value": 4}, {"source": 731, "target": 273, "value": 11}, {"source": 731, "target": 281, "value": 4}, {"source": 731, "target": 369, "value": 8}, {"source": 731, "target": 377, "value": 8}, {"source": 731, "target": 381, "value": 4}, {"source": 731, "target": 431, "value": 4}, {"source": 731, "target": 519, "value": 9}, {"source": 731, "target": 531, "value": 4}, {"source": 731, "target": 581, "value": 4}, {"source": 731, "target": 605, "value": 8}, {"source": 731, "target": 621, "value": 8}, {"source": 731, "target": 669, "value": 7}, {"source": 731, "target": 681, "value": 3}, {"source": 735, "target": 273, "value": 9}, {"source": 735, "target": 285, "value": 4}, {"source": 735, "target": 335, "value": 4}, {"source": 735, "target": 377, "value": 10}, {"source": 735, "target": 435, "value": 4}, {"source": 735, "target": 473, "value": 8}, {"source": 735, "target": 485, "value": 4}, {"source": 735, "target": 525, "value": 9}, {"source": 735, "target": 531, "value": 8}, {"source": 735, "target": 573, "value": 8}, {"source": 735, "target": 585, "value": 4}, {"source": 735, "target": 587, "value": 9}, {"source": 735, "target": 635, "value": 4}, {"source": 735, "target": 725, "value": 8}, {"source": 737, "target": 225, "value": 9}, {"source": 737, "target": 228, "value": 9}, {"source": 737, "target": 249, "value": 8}, {"source": 737, "target": 263, "value": 9}, {"source": 737, "target": 278, "value": 9}, {"source": 737, "target": 287, "value": 4}, {"source": 737, "target": 303, "value": 11}, {"source": 737, "target": 353, "value": 11}, {"source": 737, "target": 363, "value": 9}, {"source": 737, "target": 378, "value": 8}, {"source": 737, "target": 399, "value": 8}, {"source": 737, "target": 413, "value": 4}, {"source": 737, "target": 425, "value": 8}, {"source": 737, "target": 428, "value": 8}, {"source": 737, "target": 429, "value": 10}, {"source": 737, "target": 437, "value": 2}, {"source": 737, "target": 453, "value": 10}, {"source": 737, "target": 461, "value": 8}, {"source": 737, "target": 489, "value": 9}, {"source": 737, "target": 503, "value": 10}, {"source": 737, "target": 513, "value": 4}, {"source": 737, "target": 525, "value": 8}, {"source": 737, "target": 528, "value": 8}, {"source": 737, "target": 545, "value": 8}, {"source": 737, "target": 549, "value": 8}, {"source": 737, "target": 563, "value": 8}, {"source": 737, "target": 578, "value": 8}, {"source": 737, "target": 587, "value": 4}, {"source": 737, "target": 603, "value": 9}, {"source": 737, "target": 653, "value": 8}, {"source": 737, "target": 663, "value": 8}, {"source": 737, "target": 677, "value": 8}, {"source": 737, "target": 678, "value": 7}, {"source": 737, "target": 699, "value": 8}, {"source": 737, "target": 713, "value": 4}, {"source": 737, "target": 725, "value": 7}, {"source": 737, "target": 728, "value": 7}, {"source": 741, "target": 219, "value": 8}, {"source": 741, "target": 228, "value": 8}, {"source": 741, "target": 233, "value": 4}, {"source": 741, "target": 245, "value": 8}, {"source": 741, "target": 248, "value": 8}, {"source": 741, "target": 249, "value": 8}, {"source": 741, "target": 251, "value": 8}, {"source": 741, "target": 278, "value": 8}, {"source": 741, "target": 285, "value": 10}, {"source": 741, "target": 291, "value": 4}, {"source": 741, "target": 293, "value": 5}, {"source": 741, "target": 303, "value": 4}, {"source": 741, "target": 321, "value": 9}, {"source": 741, "target": 333, "value": 8}, {"source": 741, "target": 339, "value": 8}, {"source": 741, "target": 341, "value": 2}, {"source": 741, "target": 345, "value": 8}, {"source": 741, "target": 348, "value": 8}, {"source": 741, "target": 351, "value": 8}, {"source": 741, "target": 353, "value": 4}, {"source": 741, "target": 365, "value": 8}, {"source": 741, "target": 369, "value": 8}, {"source": 741, "target": 378, "value": 8}, {"source": 741, "target": 381, "value": 10}, {"source": 741, "target": 395, "value": 8}, {"source": 741, "target": 398, "value": 8}, {"source": 741, "target": 399, "value": 8}, {"source": 741, "target": 401, "value": 8}, {"source": 741, "target": 413, "value": 8}, {"source": 741, "target": 428, "value": 8}, {"source": 741, "target": 429, "value": 8}, {"source": 741, "target": 437, "value": 8}, {"source": 741, "target": 441, "value": 2}, {"source": 741, "target": 453, "value": 4}, {"source": 741, "target": 461, "value": 9}, {"source": 741, "target": 485, "value": 9}, {"source": 741, "target": 489, "value": 4}, {"source": 741, "target": 491, "value": 4}, {"source": 741, "target": 495, "value": 8}, {"source": 741, "target": 498, "value": 8}, {"source": 741, "target": 501, "value": 8}, {"source": 741, "target": 503, "value": 4}, {"source": 741, "target": 519, "value": 9}, {"source": 741, "target": 528, "value": 8}, {"source": 741, "target": 533, "value": 4}, {"source": 741, "target": 545, "value": 4}, {"source": 741, "target": 548, "value": 8}, {"source": 741, "target": 549, "value": 8}, {"source": 741, "target": 551, "value": 8}, {"source": 741, "target": 578, "value": 8}, {"source": 741, "target": 581, "value": 9}, {"source": 741, "target": 587, "value": 8}, {"source": 741, "target": 591, "value": 4}, {"source": 741, "target": 603, "value": 4}, {"source": 741, "target": 633, "value": 8}, {"source": 741, "target": 639, "value": 8}, {"source": 741, "target": 641, "value": 2}, {"source": 741, "target": 645, "value": 7}, {"source": 741, "target": 648, "value": 8}, {"source": 741, "target": 651, "value": 8}, {"source": 741, "target": 653, "value": 4}, {"source": 741, "target": 669, "value": 8}, {"source": 741, "target": 671, "value": 7}, {"source": 741, "target": 678, "value": 7}, {"source": 741, "target": 693, "value": 8}, {"source": 741, "target": 695, "value": 7}, {"source": 741, "target": 698, "value": 7}, {"source": 741, "target": 699, "value": 4}, {"source": 741, "target": 701, "value": 8}, {"source": 741, "target": 713, "value": 8}, {"source": 741, "target": 728, "value": 8}, {"source": 741, "target": 729, "value": 8}, {"source": 741, "target": 737, "value": 8}, {"source": 743, "target": 243, "value": 2}, {"source": 743, "target": 281, "value": 9}, {"source": 743, "target": 293, "value": 1}, {"source": 743, "target": 317, "value": 4}, {"source": 743, "target": 333, "value": 10}, {"source": 743, "target": 335, "value": 8}, {"source": 743, "target": 339, "value": 8}, {"source": 743, "target": 377, "value": 8}, {"source": 743, "target": 381, "value": 8}, {"source": 743, "target": 393, "value": 1}, {"source": 743, "target": 398, "value": 9}, {"source": 743, "target": 429, "value": 8}, {"source": 743, "target": 431, "value": 9}, {"source": 743, "target": 443, "value": 2}, {"source": 743, "target": 467, "value": 8}, {"source": 743, "target": 489, "value": 2}, {"source": 743, "target": 533, "value": 9}, {"source": 743, "target": 543, "value": 2}, {"source": 743, "target": 545, "value": 9}, {"source": 743, "target": 581, "value": 8}, {"source": 743, "target": 585, "value": 9}, {"source": 743, "target": 593, "value": 1}, {"source": 743, "target": 617, "value": 8}, {"source": 743, "target": 639, "value": 8}, {"source": 743, "target": 677, "value": 8}, {"source": 743, "target": 681, "value": 7}, {"source": 743, "target": 693, "value": 1}, {"source": 753, "target": 215, "value": 8}, {"source": 753, "target": 227, "value": 4}, {"source": 753, "target": 228, "value": 8}, {"source": 753, "target": 249, "value": 8}, {"source": 753, "target": 278, "value": 8}, {"source": 753, "target": 279, "value": 4}, {"source": 753, "target": 293, "value": 10}, {"source": 753, "target": 303, "value": 1}, {"source": 753, "target": 315, "value": 8}, {"source": 753, "target": 341, "value": 9}, {"source": 753, "target": 353, "value": 1}, {"source": 753, "target": 365, "value": 8}, {"source": 753, "target": 377, "value": 4}, {"source": 753, "target": 378, "value": 8}, {"source": 753, "target": 399, "value": 8}, {"source": 753, "target": 405, "value": 4}, {"source": 753, "target": 428, "value": 8}, {"source": 753, "target": 437, "value": 8}, {"source": 753, "target": 441, "value": 8}, {"source": 753, "target": 453, "value": 0}, {"source": 753, "target": 461, "value": 8}, {"source": 753, "target": 465, "value": 8}, {"source": 753, "target": 503, "value": 1}, {"source": 753, "target": 515, "value": 8}, {"source": 753, "target": 527, "value": 4}, {"source": 753, "target": 528, "value": 8}, {"source": 753, "target": 545, "value": 8}, {"source": 753, "target": 549, "value": 8}, {"source": 753, "target": 578, "value": 8}, {"source": 753, "target": 603, "value": 1}, {"source": 753, "target": 605, "value": 9}, {"source": 753, "target": 615, "value": 8}, {"source": 753, "target": 641, "value": 8}, {"source": 753, "target": 653, "value": 1}, {"source": 753, "target": 665, "value": 7}, {"source": 753, "target": 677, "value": 4}, {"source": 753, "target": 678, "value": 8}, {"source": 753, "target": 693, "value": 8}, {"source": 753, "target": 699, "value": 4}, {"source": 753, "target": 701, "value": 3}, {"source": 753, "target": 728, "value": 8}, {"source": 753, "target": 737, "value": 8}, {"source": 753, "target": 741, "value": 4}, {"source": 755, "target": 245, "value": 11}, {"source": 755, "target": 251, "value": 8}, {"source": 755, "target": 255, "value": 4}, {"source": 755, "target": 263, "value": 12}, {"source": 755, "target": 293, "value": 9}, {"source": 755, "target": 303, "value": 8}, {"source": 755, "target": 305, "value": 4}, {"source": 755, "target": 309, "value": 8}, {"source": 755, "target": 351, "value": 10}, {"source": 755, "target": 353, "value": 8}, {"source": 755, "target": 363, "value": 11}, {"source": 755, "target": 377, "value": 11}, {"source": 755, "target": 393, "value": 8}, {"source": 755, "target": 405, "value": 2}, {"source": 755, "target": 413, "value": 10}, {"source": 755, "target": 429, "value": 8}, {"source": 755, "target": 453, "value": 8}, {"source": 755, "target": 455, "value": 4}, {"source": 755, "target": 459, "value": 8}, {"source": 755, "target": 497, "value": 4}, {"source": 755, "target": 503, "value": 8}, {"source": 755, "target": 513, "value": 10}, {"source": 755, "target": 549, "value": 9}, {"source": 755, "target": 555, "value": 4}, {"source": 755, "target": 563, "value": 9}, {"source": 755, "target": 593, "value": 8}, {"source": 755, "target": 603, "value": 8}, {"source": 755, "target": 605, "value": 4}, {"source": 755, "target": 609, "value": 8}, {"source": 755, "target": 645, "value": 8}, {"source": 755, "target": 653, "value": 8}, {"source": 755, "target": 663, "value": 9}, {"source": 755, "target": 693, "value": 7}, {"source": 755, "target": 705, "value": 3}, {"source": 755, "target": 713, "value": 8}, {"source": 755, "target": 753, "value": 7}, {"source": 759, "target": 263, "value": 12}, {"source": 759, "target": 309, "value": 4}, {"source": 759, "target": 333, "value": 8}, {"source": 759, "target": 351, "value": 10}, {"source": 759, "target": 363, "value": 11}, {"source": 759, "target": 405, "value": 8}, {"source": 759, "target": 413, "value": 11}, {"source": 759, "target": 459, "value": 4}, {"source": 759, "target": 461, "value": 9}, {"source": 759, "target": 497, "value": 4}, {"source": 759, "target": 513, "value": 10}, {"source": 759, "target": 549, "value": 9}, {"source": 759, "target": 563, "value": 9}, {"source": 759, "target": 609, "value": 4}, {"source": 759, "target": 663, "value": 9}, {"source": 759, "target": 713, "value": 8}, {"source": 759, "target": 755, "value": 8}, {"source": 761, "target": 249, "value": 9}, {"source": 761, "target": 261, "value": 2}, {"source": 761, "target": 273, "value": 4}, {"source": 761, "target": 285, "value": 8}, {"source": 761, "target": 311, "value": 4}, {"source": 761, "target": 323, "value": 4}, {"source": 761, "target": 363, "value": 10}, {"source": 761, "target": 411, "value": 4}, {"source": 761, "target": 413, "value": 4}, {"source": 761, "target": 423, "value": 4}, {"source": 761, "target": 461, "value": 2}, {"source": 761, "target": 473, "value": 4}, {"source": 761, "target": 501, "value": 9}, {"source": 761, "target": 549, "value": 8}, {"source": 761, "target": 561, "value": 2}, {"source": 761, "target": 573, "value": 4}, {"source": 761, "target": 611, "value": 4}, {"source": 761, "target": 623, "value": 4}, {"source": 761, "target": 678, "value": 4}, {"source": 761, "target": 701, "value": 8}, {"source": 761, "target": 711, "value": 3}, {"source": 761, "target": 713, "value": 8}, {"source": 761, "target": 723, "value": 4}, {"source": 765, "target": 215, "value": 4}, {"source": 765, "target": 225, "value": 8}, {"source": 765, "target": 227, "value": 8}, {"source": 765, "target": 228, "value": 8}, {"source": 765, "target": 275, "value": 8}, {"source": 765, "target": 278, "value": 8}, {"source": 765, "target": 291, "value": 8}, {"source": 765, "target": 293, "value": 10}, {"source": 765, "target": 315, "value": 4}, {"source": 765, "target": 317, "value": 10}, {"source": 765, "target": 321, "value": 8}, {"source": 765, "target": 341, "value": 8}, {"source": 765, "target": 353, "value": 9}, {"source": 765, "target": 365, "value": 1}, {"source": 765, "target": 375, "value": 8}, {"source": 765, "target": 377, "value": 8}, {"source": 765, "target": 378, "value": 8}, {"source": 765, "target": 405, "value": 10}, {"source": 765, "target": 413, "value": 8}, {"source": 765, "target": 425, "value": 8}, {"source": 765, "target": 428, "value": 8}, {"source": 765, "target": 441, "value": 8}, {"source": 765, "target": 453, "value": 4}, {"source": 765, "target": 461, "value": 8}, {"source": 765, "target": 465, "value": 2}, {"source": 765, "target": 491, "value": 8}, {"source": 765, "target": 513, "value": 8}, {"source": 765, "target": 515, "value": 4}, {"source": 765, "target": 525, "value": 8}, {"source": 765, "target": 527, "value": 8}, {"source": 765, "target": 528, "value": 8}, {"source": 765, "target": 563, "value": 8}, {"source": 765, "target": 573, "value": 8}, {"source": 765, "target": 575, "value": 8}, {"source": 765, "target": 578, "value": 8}, {"source": 765, "target": 591, "value": 8}, {"source": 765, "target": 605, "value": 9}, {"source": 765, "target": 615, "value": 4}, {"source": 765, "target": 641, "value": 8}, {"source": 765, "target": 653, "value": 8}, {"source": 765, "target": 665, "value": 2}, {"source": 765, "target": 671, "value": 8}, {"source": 765, "target": 675, "value": 8}, {"source": 765, "target": 677, "value": 7}, {"source": 765, "target": 678, "value": 8}, {"source": 765, "target": 713, "value": 4}, {"source": 765, "target": 725, "value": 8}, {"source": 765, "target": 728, "value": 8}, {"source": 765, "target": 741, "value": 8}, {"source": 765, "target": 753, "value": 7}, {"source": 767, "target": 225, "value": 8}, {"source": 767, "target": 228, "value": 8}, {"source": 767, "target": 243, "value": 9}, {"source": 767, "target": 255, "value": 10}, {"source": 767, "target": 275, "value": 8}, {"source": 767, "target": 278, "value": 8}, {"source": 767, "target": 293, "value": 4}, {"source": 767, "target": 305, "value": 9}, {"source": 767, "target": 317, "value": 4}, {"source": 767, "target": 321, "value": 8}, {"source": 767, "target": 363, "value": 8}, {"source": 767, "target": 365, "value": 9}, {"source": 767, "target": 375, "value": 8}, {"source": 767, "target": 378, "value": 8}, {"source": 767, "target": 393, "value": 4}, {"source": 767, "target": 405, "value": 8}, {"source": 767, "target": 413, "value": 8}, {"source": 767, "target": 425, "value": 8}, {"source": 767, "target": 428, "value": 8}, {"source": 767, "target": 443, "value": 9}, {"source": 767, "target": 467, "value": 4}, {"source": 767, "target": 489, "value": 10}, {"source": 767, "target": 513, "value": 8}, {"source": 767, "target": 525, "value": 8}, {"source": 767, "target": 528, "value": 8}, {"source": 767, "target": 543, "value": 8}, {"source": 767, "target": 557, "value": 9}, {"source": 767, "target": 563, "value": 8}, {"source": 767, "target": 575, "value": 8}, {"source": 767, "target": 578, "value": 8}, {"source": 767, "target": 593, "value": 4}, {"source": 767, "target": 605, "value": 8}, {"source": 767, "target": 617, "value": 4}, {"source": 767, "target": 671, "value": 8}, {"source": 767, "target": 675, "value": 8}, {"source": 767, "target": 678, "value": 8}, {"source": 767, "target": 693, "value": 4}, {"source": 767, "target": 705, "value": 7}, {"source": 767, "target": 713, "value": 4}, {"source": 767, "target": 725, "value": 8}, {"source": 767, "target": 728, "value": 8}, {"source": 767, "target": 743, "value": 8}, {"source": 767, "target": 765, "value": 7}, {"source": 771, "target": 221, "value": 4}, {"source": 771, "target": 261, "value": 11}, {"source": 771, "target": 293, "value": 12}, {"source": 771, "target": 309, "value": 9}, {"source": 771, "target": 321, "value": 4}, {"source": 771, "target": 371, "value": 4}, {"source": 771, "target": 461, "value": 10}, {"source": 771, "target": 471, "value": 4}, {"source": 771, "target": 521, "value": 4}, {"source": 771, "target": 573, "value": 9}, {"source": 771, "target": 609, "value": 8}, {"source": 771, "target": 621, "value": 4}, {"source": 771, "target": 663, "value": 9}, {"source": 771, "target": 671, "value": 4}, {"source": 773, "target": 219, "value": 8}, {"source": 773, "target": 221, "value": 2}, {"source": 773, "target": 233, "value": 8}, {"source": 773, "target": 248, "value": 8}, {"source": 773, "target": 257, "value": 8}, {"source": 773, "target": 261, "value": 9}, {"source": 773, "target": 273, "value": 1}, {"source": 773, "target": 279, "value": 8}, {"source": 773, "target": 285, "value": 8}, {"source": 773, "target": 309, "value": 9}, {"source": 773, "target": 321, "value": 4}, {"source": 773, "target": 323, "value": 1}, {"source": 773, "target": 333, "value": 8}, {"source": 773, "target": 335, "value": 8}, {"source": 773, "target": 347, "value": 4}, {"source": 773, "target": 348, "value": 8}, {"source": 773, "target": 369, "value": 8}, {"source": 773, "target": 377, "value": 10}, {"source": 773, "target": 383, "value": 8}, {"source": 773, "target": 398, "value": 8}, {"source": 773, "target": 413, "value": 10}, {"source": 773, "target": 423, "value": 1}, {"source": 773, "target": 435, "value": 8}, {"source": 773, "target": 461, "value": 8}, {"source": 773, "target": 473, "value": 1}, {"source": 773, "target": 483, "value": 8}, {"source": 773, "target": 485, "value": 8}, {"source": 773, "target": 497, "value": 4}, {"source": 773, "target": 498, "value": 8}, {"source": 773, "target": 519, "value": 8}, {"source": 773, "target": 521, "value": 8}, {"source": 773, "target": 525, "value": 9}, {"source": 773, "target": 531, "value": 8}, {"source": 773, "target": 533, "value": 8}, {"source": 773, "target": 545, "value": 10}, {"source": 773, "target": 548, "value": 8}, {"source": 773, "target": 557, "value": 8}, {"source": 773, "target": 561, "value": 8}, {"source": 773, "target": 573, "value": 0}, {"source": 773, "target": 585, "value": 8}, {"source": 773, "target": 611, "value": 8}, {"source": 773, "target": 615, "value": 8}, {"source": 773, "target": 621, "value": 2}, {"source": 773, "target": 623, "value": 1}, {"source": 773, "target": 633, "value": 8}, {"source": 773, "target": 635, "value": 8}, {"source": 773, "target": 647, "value": 4}, {"source": 773, "target": 648, "value": 8}, {"source": 773, "target": 669, "value": 8}, {"source": 773, "target": 678, "value": 8}, {"source": 773, "target": 683, "value": 8}, {"source": 773, "target": 698, "value": 7}, {"source": 773, "target": 699, "value": 4}, {"source": 773, "target": 723, "value": 1}, {"source": 773, "target": 725, "value": 8}, {"source": 773, "target": 735, "value": 8}, {"source": 773, "target": 761, "value": 4}, {"source": 783, "target": 221, "value": 4}, {"source": 783, "target": 225, "value": 9}, {"source": 783, "target": 231, "value": 8}, {"source": 783, "target": 233, "value": 1}, {"source": 783, "target": 245, "value": 8}, {"source": 783, "target": 248, "value": 8}, {"source": 783, "target": 257, "value": 8}, {"source": 783, "target": 279, "value": 4}, {"source": 783, "target": 281, "value": 8}, {"source": 783, "target": 285, "value": 10}, {"source": 783, "target": 287, "value": 8}, {"source": 783, "target": 317, "value": 8}, {"source": 783, "target": 321, "value": 4}, {"source": 783, "target": 333, "value": 1}, {"source": 783, "target": 335, "value": 10}, {"source": 783, "target": 345, "value": 8}, {"source": 783, "target": 348, "value": 8}, {"source": 783, "target": 381, "value": 4}, {"source": 783, "target": 383, "value": 2}, {"source": 783, "target": 395, "value": 8}, {"source": 783, "target": 398, "value": 8}, {"source": 783, "target": 405, "value": 11}, {"source": 783, "target": 407, "value": 8}, {"source": 783, "target": 425, "value": 8}, {"source": 783, "target": 429, "value": 4}, {"source": 783, "target": 431, "value": 8}, {"source": 783, "target": 437, "value": 8}, {"source": 783, "target": 483, "value": 2}, {"source": 783, "target": 485, "value": 9}, {"source": 783, "target": 495, "value": 8}, {"source": 783, "target": 498, "value": 8}, {"source": 783, "target": 521, "value": 8}, {"source": 783, "target": 525, "value": 8}, {"source": 783, "target": 531, "value": 8}, {"source": 783, "target": 533, "value": 1}, {"source": 783, "target": 545, "value": 8}, {"source": 783, "target": 548, "value": 8}, {"source": 783, "target": 557, "value": 4}, {"source": 783, "target": 573, "value": 9}, {"source": 783, "target": 579, "value": 8}, {"source": 783, "target": 581, "value": 8}, {"source": 783, "target": 587, "value": 8}, {"source": 783, "target": 605, "value": 8}, {"source": 783, "target": 617, "value": 8}, {"source": 783, "target": 621, "value": 8}, {"source": 783, "target": 633, "value": 1}, {"source": 783, "target": 645, "value": 8}, {"source": 783, "target": 648, "value": 8}, {"source": 783, "target": 669, "value": 8}, {"source": 783, "target": 677, "value": 8}, {"source": 783, "target": 681, "value": 8}, {"source": 783, "target": 683, "value": 2}, {"source": 783, "target": 695, "value": 8}, {"source": 783, "target": 698, "value": 8}, {"source": 783, "target": 707, "value": 8}, {"source": 783, "target": 725, "value": 8}, {"source": 783, "target": 729, "value": 8}, {"source": 783, "target": 731, "value": 7}, {"source": 783, "target": 737, "value": 7}, {"source": 783, "target": 741, "value": 8}, {"source": 783, "target": 773, "value": 7}, {"source": 785, "target": 273, "value": 9}, {"source": 785, "target": 285, "value": 2}, {"source": 785, "target": 293, "value": 8}, {"source": 785, "target": 335, "value": 4}, {"source": 785, "target": 347, "value": 8}, {"source": 785, "target": 377, "value": 10}, {"source": 785, "target": 435, "value": 4}, {"source": 785, "target": 437, "value": 9}, {"source": 785, "target": 473, "value": 8}, {"source": 785, "target": 485, "value": 2}, {"source": 785, "target": 497, "value": 8}, {"source": 785, "target": 525, "value": 9}, {"source": 785, "target": 531, "value": 8}, {"source": 785, "target": 573, "value": 8}, {"source": 785, "target": 585, "value": 2}, {"source": 785, "target": 587, "value": 9}, {"source": 785, "target": 635, "value": 4}, {"source": 785, "target": 647, "value": 8}, {"source": 785, "target": 725, "value": 8}, {"source": 785, "target": 735, "value": 3}, {"source": 785, "target": 773, "value": 7}, {"source": 789, "target": 228, "value": 8}, {"source": 789, "target": 243, "value": 12}, {"source": 789, "target": 249, "value": 8}, {"source": 789, "target": 251, "value": 8}, {"source": 789, "target": 278, "value": 8}, {"source": 789, "target": 293, "value": 12}, {"source": 789, "target": 303, "value": 10}, {"source": 789, "target": 335, "value": 8}, {"source": 789, "target": 339, "value": 4}, {"source": 789, "target": 341, "value": 10}, {"source": 789, "target": 351, "value": 8}, {"source": 789, "target": 353, "value": 10}, {"source": 789, "target": 377, "value": 8}, {"source": 789, "target": 378, "value": 8}, {"source": 789, "target": 393, "value": 11}, {"source": 789, "target": 399, "value": 8}, {"source": 789, "target": 401, "value": 8}, {"source": 789, "target": 413, "value": 8}, {"source": 789, "target": 428, "value": 8}, {"source": 789, "target": 429, "value": 10}, {"source": 789, "target": 437, "value": 8}, {"source": 789, "target": 443, "value": 10}, {"source": 789, "target": 453, "value": 9}, {"source": 789, "target": 461, "value": 10}, {"source": 789, "target": 489, "value": 2}, {"source": 789, "target": 501, "value": 8}, {"source": 789, "target": 503, "value": 9}, {"source": 789, "target": 528, "value": 8}, {"source": 789, "target": 543, "value": 10}, {"source": 789, "target": 545, "value": 8}, {"source": 789, "target": 549, "value": 8}, {"source": 789, "target": 551, "value": 8}, {"source": 789, "target": 578, "value": 8}, {"source": 789, "target": 593, "value": 9}, {"source": 789, "target": 603, "value": 8}, {"source": 789, "target": 639, "value": 4}, {"source": 789, "target": 651, "value": 8}, {"source": 789, "target": 653, "value": 8}, {"source": 789, "target": 677, "value": 8}, {"source": 789, "target": 678, "value": 7}, {"source": 789, "target": 693, "value": 9}, {"source": 789, "target": 699, "value": 8}, {"source": 789, "target": 701, "value": 7}, {"source": 789, "target": 728, "value": 7}, {"source": 789, "target": 737, "value": 7}, {"source": 789, "target": 741, "value": 2}, {"source": 789, "target": 743, "value": 8}, {"source": 789, "target": 753, "value": 8}, {"source": 791, "target": 291, "value": 4}, {"source": 791, "target": 293, "value": 10}, {"source": 791, "target": 341, "value": 4}, {"source": 791, "target": 365, "value": 8}, {"source": 791, "target": 381, "value": 10}, {"source": 791, "target": 429, "value": 8}, {"source": 791, "target": 441, "value": 4}, {"source": 791, "target": 491, "value": 4}, {"source": 791, "target": 533, "value": 9}, {"source": 791, "target": 581, "value": 9}, {"source": 791, "target": 587, "value": 8}, {"source": 791, "target": 591, "value": 4}, {"source": 791, "target": 641, "value": 4}, {"source": 791, "target": 713, "value": 9}, {"source": 791, "target": 729, "value": 7}, {"source": 791, "target": 741, "value": 3}, {"source": 791, "target": 765, "value": 8}, {"source": 795, "target": 233, "value": 4}, {"source": 795, "target": 245, "value": 4}, {"source": 795, "target": 248, "value": 8}, {"source": 795, "target": 285, "value": 10}, {"source": 795, "target": 333, "value": 9}, {"source": 795, "target": 345, "value": 4}, {"source": 795, "target": 348, "value": 8}, {"source": 795, "target": 395, "value": 4}, {"source": 795, "target": 398, "value": 8}, {"source": 795, "target": 485, "value": 9}, {"source": 795, "target": 495, "value": 4}, {"source": 795, "target": 498, "value": 8}, {"source": 795, "target": 533, "value": 8}, {"source": 795, "target": 545, "value": 4}, {"source": 795, "target": 548, "value": 8}, {"source": 795, "target": 633, "value": 8}, {"source": 795, "target": 645, "value": 4}, {"source": 795, "target": 648, "value": 7}, {"source": 795, "target": 669, "value": 8}, {"source": 795, "target": 695, "value": 4}, {"source": 795, "target": 698, "value": 7}, {"source": 795, "target": 741, "value": 7}, {"source": 795, "target": 783, "value": 8}, {"source": 797, "target": 221, "value": 9}, {"source": 797, "target": 225, "value": 8}, {"source": 797, "target": 245, "value": 4}, {"source": 797, "target": 251, "value": 8}, {"source": 797, "target": 257, "value": 8}, {"source": 797, "target": 263, "value": 12}, {"source": 797, "target": 273, "value": 3}, {"source": 797, "target": 275, "value": 8}, {"source": 797, "target": 285, "value": 9}, {"source": 797, "target": 293, "value": 8}, {"source": 797, "target": 309, "value": 8}, {"source": 797, "target": 323, "value": 4}, {"source": 797, "target": 341, "value": 10}, {"source": 797, "target": 345, "value": 4}, {"source": 797, "target": 347, "value": 4}, {"source": 797, "target": 351, "value": 4}, {"source": 797, "target": 363, "value": 11}, {"source": 797, "target": 375, "value": 8}, {"source": 797, "target": 377, "value": 9}, {"source": 797, "target": 395, "value": 8}, {"source": 797, "target": 401, "value": 8}, {"source": 797, "target": 405, "value": 8}, {"source": 797, "target": 407, "value": 8}, {"source": 797, "target": 413, "value": 10}, {"source": 797, "target": 423, "value": 4}, {"source": 797, "target": 425, "value": 8}, {"source": 797, "target": 437, "value": 10}, {"source": 797, "target": 459, "value": 8}, {"source": 797, "target": 473, "value": 2}, {"source": 797, "target": 485, "value": 8}, {"source": 797, "target": 489, "value": 8}, {"source": 797, "target": 495, "value": 8}, {"source": 797, "target": 497, "value": 2}, {"source": 797, "target": 501, "value": 8}, {"source": 797, "target": 503, "value": 8}, {"source": 797, "target": 513, "value": 10}, {"source": 797, "target": 525, "value": 8}, {"source": 797, "target": 545, "value": 2}, {"source": 797, "target": 549, "value": 9}, {"source": 797, "target": 551, "value": 8}, {"source": 797, "target": 557, "value": 8}, {"source": 797, "target": 563, "value": 9}, {"source": 797, "target": 573, "value": 2}, {"source": 797, "target": 575, "value": 8}, {"source": 797, "target": 585, "value": 4}, {"source": 797, "target": 609, "value": 8}, {"source": 797, "target": 621, "value": 8}, {"source": 797, "target": 623, "value": 4}, {"source": 797, "target": 645, "value": 4}, {"source": 797, "target": 647, "value": 4}, {"source": 797, "target": 651, "value": 8}, {"source": 797, "target": 663, "value": 9}, {"source": 797, "target": 669, "value": 7}, {"source": 797, "target": 675, "value": 8}, {"source": 797, "target": 695, "value": 4}, {"source": 797, "target": 699, "value": 8}, {"source": 797, "target": 701, "value": 8}, {"source": 797, "target": 707, "value": 8}, {"source": 797, "target": 713, "value": 8}, {"source": 797, "target": 723, "value": 4}, {"source": 797, "target": 725, "value": 8}, {"source": 797, "target": 741, "value": 8}, {"source": 797, "target": 755, "value": 7}, {"source": 797, "target": 759, "value": 8}, {"source": 797, "target": 773, "value": 2}, {"source": 797, "target": 785, "value": 7}, {"source": 797, "target": 789, "value": 7}, {"source": 797, "target": 795, "value": 7}, {"source": 798, "target": 219, "value": 8}, {"source": 798, "target": 233, "value": 5}, {"source": 798, "target": 245, "value": 8}, {"source": 798, "target": 248, "value": 4}, {"source": 798, "target": 257, "value": 8}, {"source": 798, "target": 273, "value": 11}, {"source": 798, "target": 285, "value": 11}, {"source": 798, "target": 309, "value": 10}, {"source": 798, "target": 323, "value": 11}, {"source": 798, "target": 333, "value": 9}, {"source": 798, "target": 345, "value": 8}, {"source": 798, "target": 348, "value": 4}, {"source": 798, "target": 369, "value": 8}, {"source": 798, "target": 395, "value": 8}, {"source": 798, "target": 398, "value": 4}, {"source": 798, "target": 423, "value": 10}, {"source": 798, "target": 473, "value": 10}, {"source": 798, "target": 485, "value": 10}, {"source": 798, "target": 495, "value": 8}, {"source": 798, "target": 498, "value": 4}, {"source": 798, "target": 519, "value": 8}, {"source": 798, "target": 533, "value": 8}, {"source": 798, "target": 545, "value": 8}, {"source": 798, "target": 548, "value": 4}, {"source": 798, "target": 557, "value": 8}, {"source": 798, "target": 573, "value": 9}, {"source": 798, "target": 611, "value": 9}, {"source": 798, "target": 615, "value": 8}, {"source": 798, "target": 623, "value": 9}, {"source": 798, "target": 633, "value": 8}, {"source": 798, "target": 645, "value": 8}, {"source": 798, "target": 648, "value": 4}, {"source": 798, "target": 669, "value": 8}, {"source": 798, "target": 695, "value": 8}, {"source": 798, "target": 698, "value": 4}, {"source": 798, "target": 723, "value": 8}, {"source": 798, "target": 741, "value": 7}, {"source": 798, "target": 773, "value": 8}, {"source": 798, "target": 783, "value": 8}, {"source": 798, "target": 795, "value": 8}, {"source": 801, "target": 251, "value": 4}, {"source": 801, "target": 263, "value": 8}, {"source": 801, "target": 341, "value": 10}, {"source": 801, "target": 351, "value": 4}, {"source": 801, "target": 363, "value": 8}, {"source": 801, "target": 401, "value": 2}, {"source": 801, "target": 413, "value": 8}, {"source": 801, "target": 453, "value": 9}, {"source": 801, "target": 455, "value": 10}, {"source": 801, "target": 489, "value": 8}, {"source": 801, "target": 501, "value": 2}, {"source": 801, "target": 503, "value": 9}, {"source": 801, "target": 513, "value": 8}, {"source": 801, "target": 525, "value": 8}, {"source": 801, "target": 551, "value": 4}, {"source": 801, "target": 563, "value": 8}, {"source": 801, "target": 573, "value": 10}, {"source": 801, "target": 651, "value": 2}, {"source": 801, "target": 653, "value": 8}, {"source": 801, "target": 663, "value": 8}, {"source": 801, "target": 701, "value": 2}, {"source": 801, "target": 713, "value": 7}, {"source": 801, "target": 741, "value": 8}, {"source": 801, "target": 789, "value": 7}, {"source": 801, "target": 797, "value": 8}, {"source": 803, "target": 227, "value": 8}, {"source": 803, "target": 228, "value": 8}, {"source": 803, "target": 249, "value": 8}, {"source": 803, "target": 278, "value": 8}, {"source": 803, "target": 279, "value": 10}, {"source": 803, "target": 293, "value": 10}, {"source": 803, "target": 303, "value": 2}, {"source": 803, "target": 341, "value": 9}, {"source": 803, "target": 353, "value": 1}, {"source": 803, "target": 377, "value": 8}, {"source": 803, "target": 378, "value": 8}, {"source": 803, "target": 399, "value": 8}, {"source": 803, "target": 405, "value": 10}, {"source": 803, "target": 428, "value": 8}, {"source": 803, "target": 437, "value": 8}, {"source": 803, "target": 441, "value": 8}, {"source": 803, "target": 453, "value": 1}, {"source": 803, "target": 503, "value": 2}, {"source": 803, "target": 527, "value": 8}, {"source": 803, "target": 528, "value": 8}, {"source": 803, "target": 545, "value": 8}, {"source": 803, "target": 549, "value": 8}, {"source": 803, "target": 578, "value": 8}, {"source": 803, "target": 603, "value": 2}, {"source": 803, "target": 641, "value": 8}, {"source": 803, "target": 653, "value": 1}, {"source": 803, "target": 677, "value": 8}, {"source": 803, "target": 678, "value": 8}, {"source": 803, "target": 693, "value": 8}, {"source": 803, "target": 699, "value": 4}, {"source": 803, "target": 701, "value": 8}, {"source": 803, "target": 728, "value": 7}, {"source": 803, "target": 737, "value": 8}, {"source": 803, "target": 741, "value": 3}, {"source": 803, "target": 753, "value": 1}, {"source": 803, "target": 755, "value": 8}, {"source": 803, "target": 789, "value": 7}]}
\ No newline at end of file
+{"nodes": [{"id": 215, "name": "Citizen n\u00b0202", "mass": 10}, {"id": 219, "name": "Citizen n\u00b0206", "mass": 10}, {"id": 221, "name": "Citizen n\u00b0208", "mass": 11}, {"id": 225, "name": "Citizen n\u00b0212", "mass": 9}, {"id": 227, "name": "Citizen n\u00b0214", "mass": 5}, {"id": 228, "name": "Citizen n\u00b0215", "mass": 8}, {"id": 231, "name": "Citizen n\u00b0218", "mass": 5}, {"id": 233, "name": "Citizen n\u00b0220", "mass": 10}, {"id": 243, "name": "Citizen n\u00b0230", "mass": 8}, {"id": 245, "name": "Citizen n\u00b0232", "mass": 9}, {"id": 248, "name": "Citizen n\u00b0235", "mass": 5}, {"id": 249, "name": "Citizen n\u00b0236", "mass": 8}, {"id": 251, "name": "Citizen n\u00b0238", "mass": 9}, {"id": 255, "name": "Citizen n\u00b0242", "mass": 10}, {"id": 257, "name": "Citizen n\u00b0244", "mass": 10}, {"id": 261, "name": "Citizen n\u00b0248", "mass": 10}, {"id": 263, "name": "Citizen n\u00b0250", "mass": 9}, {"id": 273, "name": "Citizen n\u00b0260", "mass": 10}, {"id": 275, "name": "Citizen n\u00b0262", "mass": 11}, {"id": 278, "name": "Citizen n\u00b0265", "mass": 10}, {"id": 279, "name": "Citizen n\u00b0266", "mass": 10}, {"id": 281, "name": "Citizen n\u00b0268", "mass": 10}, {"id": 285, "name": "Citizen n\u00b0272", "mass": 5}, {"id": 287, "name": "Citizen n\u00b0274", "mass": 9}, {"id": 291, "name": "Citizen n\u00b0278", "mass": 9}, {"id": 293, "name": "Citizen n\u00b0280", "mass": 10}, {"id": 303, "name": "Citizen n\u00b0290", "mass": 8}, {"id": 305, "name": "Citizen n\u00b0292", "mass": 9}, {"id": 309, "name": "Citizen n\u00b0296", "mass": 9}, {"id": 311, "name": "Citizen n\u00b0298", "mass": 8}, {"id": 315, "name": "Citizen n\u00b0302", "mass": 10}, {"id": 317, "name": "Citizen n\u00b0304", "mass": 10}, {"id": 321, "name": "Citizen n\u00b0308", "mass": 11}, {"id": 323, "name": "Citizen n\u00b0310", "mass": 6}, {"id": 333, "name": "Citizen n\u00b0320", "mass": 10}, {"id": 335, "name": "Citizen n\u00b0322", "mass": 10}, {"id": 339, "name": "Citizen n\u00b0326", "mass": 10}, {"id": 341, "name": "Citizen n\u00b0328", "mass": 10}, {"id": 345, "name": "Citizen n\u00b0332", "mass": 9}, {"id": 347, "name": "Citizen n\u00b0334", "mass": 8}, {"id": 348, "name": "Citizen n\u00b0335", "mass": 5}, {"id": 351, "name": "Citizen n\u00b0338", "mass": 9}, {"id": 353, "name": "Citizen n\u00b0340", "mass": 10}, {"id": 363, "name": "Citizen n\u00b0350", "mass": 9}, {"id": 365, "name": "Citizen n\u00b0352", "mass": 5}, {"id": 369, "name": "Citizen n\u00b0356", "mass": 8}, {"id": 371, "name": "Citizen n\u00b0358", "mass": 9}, {"id": 375, "name": "Citizen n\u00b0362", "mass": 11}, {"id": 377, "name": "Citizen n\u00b0364", "mass": 10}, {"id": 378, "name": "Citizen n\u00b0365", "mass": 10}, {"id": 381, "name": "Citizen n\u00b0368", "mass": 10}, {"id": 383, "name": "Citizen n\u00b0370", "mass": 10}, {"id": 393, "name": "Citizen n\u00b0380", "mass": 10}, {"id": 395, "name": "Citizen n\u00b0382", "mass": 10}, {"id": 398, "name": "Citizen n\u00b0385", "mass": 10}, {"id": 399, "name": "Citizen n\u00b0386", "mass": 10}, {"id": 401, "name": "Citizen n\u00b0388", "mass": 10}, {"id": 405, "name": "Citizen n\u00b0392", "mass": 9}, {"id": 407, "name": "Citizen n\u00b0394", "mass": 8}, {"id": 411, "name": "Citizen n\u00b0398", "mass": 8}, {"id": 413, "name": "Citizen n\u00b0400", "mass": 11}, {"id": 423, "name": "Citizen n\u00b0410", "mass": 6}, {"id": 425, "name": "Citizen n\u00b0412", "mass": 9}, {"id": 428, "name": "Citizen n\u00b0415", "mass": 8}, {"id": 429, "name": "Citizen n\u00b0416", "mass": 10}, {"id": 431, "name": "Citizen n\u00b0418", "mass": 5}, {"id": 435, "name": "Citizen n\u00b0422", "mass": 10}, {"id": 437, "name": "Citizen n\u00b0424", "mass": 11}, {"id": 441, "name": "Citizen n\u00b0428", "mass": 10}, {"id": 443, "name": "Citizen n\u00b0430", "mass": 8}, {"id": 453, "name": "Citizen n\u00b0440", "mass": 10}, {"id": 455, "name": "Citizen n\u00b0442", "mass": 10}, {"id": 459, "name": "Citizen n\u00b0446", "mass": 10}, {"id": 461, "name": "Citizen n\u00b0448", "mass": 10}, {"id": 465, "name": "Citizen n\u00b0452", "mass": 5}, {"id": 467, "name": "Citizen n\u00b0454", "mass": 10}, {"id": 471, "name": "Citizen n\u00b0458", "mass": 9}, {"id": 473, "name": "Citizen n\u00b0460", "mass": 10}, {"id": 483, "name": "Citizen n\u00b0470", "mass": 10}, {"id": 485, "name": "Citizen n\u00b0472", "mass": 5}, {"id": 489, "name": "Citizen n\u00b0476", "mass": 8}, {"id": 491, "name": "Citizen n\u00b0478", "mass": 9}, {"id": 495, "name": "Citizen n\u00b0482", "mass": 10}, {"id": 497, "name": "Citizen n\u00b0484", "mass": 10}, {"id": 498, "name": "Citizen n\u00b0485", "mass": 10}, {"id": 501, "name": "Citizen n\u00b0488", "mass": 10}, {"id": 503, "name": "Citizen n\u00b0490", "mass": 8}, {"id": 513, "name": "Citizen n\u00b0500", "mass": 11}, {"id": 515, "name": "Citizen n\u00b0502", "mass": 10}, {"id": 519, "name": "Citizen n\u00b0506", "mass": 10}, {"id": 521, "name": "Citizen n\u00b0508", "mass": 11}, {"id": 525, "name": "Citizen n\u00b0512", "mass": 9}, {"id": 527, "name": "Citizen n\u00b0514", "mass": 5}, {"id": 528, "name": "Citizen n\u00b0515", "mass": 8}, {"id": 531, "name": "Citizen n\u00b0518", "mass": 5}, {"id": 533, "name": "Citizen n\u00b0520", "mass": 10}, {"id": 543, "name": "Citizen n\u00b0530", "mass": 8}, {"id": 545, "name": "Citizen n\u00b0532", "mass": 9}, {"id": 548, "name": "Citizen n\u00b0535", "mass": 5}, {"id": 549, "name": "Citizen n\u00b0536", "mass": 8}, {"id": 551, "name": "Citizen n\u00b0538", "mass": 9}, {"id": 555, "name": "Citizen n\u00b0542", "mass": 10}, {"id": 557, "name": "Citizen n\u00b0544", "mass": 10}, {"id": 561, "name": "Citizen n\u00b0548", "mass": 10}, {"id": 563, "name": "Citizen n\u00b0550", "mass": 9}, {"id": 573, "name": "Citizen n\u00b0560", "mass": 10}, {"id": 575, "name": "Citizen n\u00b0562", "mass": 11}, {"id": 578, "name": "Citizen n\u00b0565", "mass": 10}, {"id": 579, "name": "Citizen n\u00b0566", "mass": 10}, {"id": 581, "name": "Citizen n\u00b0568", "mass": 10}, {"id": 585, "name": "Citizen n\u00b0572", "mass": 5}, {"id": 587, "name": "Citizen n\u00b0574", "mass": 9}, {"id": 591, "name": "Citizen n\u00b0578", "mass": 9}, {"id": 593, "name": "Citizen n\u00b0580", "mass": 10}, {"id": 603, "name": "Citizen n\u00b0590", "mass": 8}, {"id": 605, "name": "Citizen n\u00b0592", "mass": 9}, {"id": 609, "name": "Citizen n\u00b0596", "mass": 9}, {"id": 611, "name": "Citizen n\u00b0598", "mass": 8}, {"id": 615, "name": "Citizen n\u00b0602", "mass": 10}, {"id": 617, "name": "Citizen n\u00b0604", "mass": 10}, {"id": 621, "name": "Citizen n\u00b0608", "mass": 11}, {"id": 623, "name": "Citizen n\u00b0610", "mass": 6}, {"id": 633, "name": "Citizen n\u00b0620", "mass": 10}, {"id": 635, "name": "Citizen n\u00b0622", "mass": 10}, {"id": 639, "name": "Citizen n\u00b0626", "mass": 10}, {"id": 641, "name": "Citizen n\u00b0628", "mass": 10}, {"id": 645, "name": "Citizen n\u00b0632", "mass": 9}, {"id": 647, "name": "Citizen n\u00b0634", "mass": 8}, {"id": 648, "name": "Citizen n\u00b0635", "mass": 5}, {"id": 651, "name": "Citizen n\u00b0638", "mass": 9}, {"id": 653, "name": "Citizen n\u00b0640", "mass": 10}, {"id": 663, "name": "Citizen n\u00b0650", "mass": 9}, {"id": 665, "name": "Citizen n\u00b0652", "mass": 5}, {"id": 669, "name": "Citizen n\u00b0656", "mass": 8}, {"id": 671, "name": "Citizen n\u00b0658", "mass": 9}, {"id": 675, "name": "Citizen n\u00b0662", "mass": 11}, {"id": 677, "name": "Citizen n\u00b0664", "mass": 10}, {"id": 678, "name": "Citizen n\u00b0665", "mass": 10}, {"id": 681, "name": "Citizen n\u00b0668", "mass": 10}, {"id": 683, "name": "Citizen n\u00b0670", "mass": 10}, {"id": 693, "name": "Citizen n\u00b0680", "mass": 10}, {"id": 695, "name": "Citizen n\u00b0682", "mass": 10}, {"id": 698, "name": "Citizen n\u00b0685", "mass": 10}, {"id": 699, "name": "Citizen n\u00b0686", "mass": 10}, {"id": 701, "name": "Citizen n\u00b0688", "mass": 10}, {"id": 705, "name": "Citizen n\u00b0692", "mass": 9}, {"id": 707, "name": "Citizen n\u00b0694", "mass": 8}, {"id": 711, "name": "Citizen n\u00b0698", "mass": 8}, {"id": 713, "name": "Citizen n\u00b0700", "mass": 11}, {"id": 723, "name": "Citizen n\u00b0710", "mass": 6}, {"id": 725, "name": "Citizen n\u00b0712", "mass": 9}, {"id": 728, "name": "Citizen n\u00b0715", "mass": 8}, {"id": 729, "name": "Citizen n\u00b0716", "mass": 10}, {"id": 731, "name": "Citizen n\u00b0718", "mass": 5}, {"id": 735, "name": "Citizen n\u00b0722", "mass": 10}, {"id": 737, "name": "Citizen n\u00b0724", "mass": 11}, {"id": 741, "name": "Citizen n\u00b0728", "mass": 10}, {"id": 743, "name": "Citizen n\u00b0730", "mass": 8}, {"id": 753, "name": "Citizen n\u00b0740", "mass": 10}, {"id": 755, "name": "Citizen n\u00b0742", "mass": 10}, {"id": 759, "name": "Citizen n\u00b0746", "mass": 10}, {"id": 761, "name": "Citizen n\u00b0748", "mass": 10}, {"id": 765, "name": "Citizen n\u00b0752", "mass": 5}, {"id": 767, "name": "Citizen n\u00b0754", "mass": 10}, {"id": 771, "name": "Citizen n\u00b0758", "mass": 9}, {"id": 773, "name": "Citizen n\u00b0760", "mass": 10}, {"id": 783, "name": "Citizen n\u00b0770", "mass": 10}, {"id": 785, "name": "Citizen n\u00b0772", "mass": 5}, {"id": 789, "name": "Citizen n\u00b0776", "mass": 8}, {"id": 791, "name": "Citizen n\u00b0778", "mass": 9}, {"id": 795, "name": "Citizen n\u00b0782", "mass": 10}, {"id": 797, "name": "Citizen n\u00b0784", "mass": 10}, {"id": 798, "name": "Citizen n\u00b0785", "mass": 10}, {"id": 801, "name": "Citizen n\u00b0788", "mass": 10}, {"id": 803, "name": "Citizen n\u00b0790", "mass": 8}], "links": [{"source": 228, "target": 225, "value": 8}, {"source": 231, "target": 221, "value": 9}, {"source": 233, "target": 215, "value": 3}, {"source": 233, "target": 221, "value": 4}, {"source": 245, "target": 233, "value": 4}, {"source": 248, "target": 219, "value": 9}, {"source": 248, "target": 233, "value": 4}, {"source": 248, "target": 245, "value": 8}, {"source": 249, "target": 228, "value": 8}, {"source": 251, "target": 245, "value": 10}, {"source": 251, "target": 249, "value": 9}, {"source": 255, "target": 245, "value": 9}, {"source": 255, "target": 251, "value": 7}, {"source": 257, "target": 219, "value": 8}, {"source": 257, "target": 233, "value": 4}, {"source": 257, "target": 245, "value": 8}, {"source": 257, "target": 248, "value": 7}, {"source": 261, "target": 221, "value": 10}, {"source": 261, "target": 249, "value": 8}, {"source": 273, "target": 219, "value": 10}, {"source": 273, "target": 221, "value": 2}, {"source": 273, "target": 231, "value": 9}, {"source": 273, "target": 248, "value": 9}, {"source": 273, "target": 257, "value": 9}, {"source": 273, "target": 261, "value": 8}, {"source": 275, "target": 225, "value": 3}, {"source": 275, "target": 228, "value": 8}, {"source": 275, "target": 257, "value": 3}, {"source": 278, "target": 225, "value": 7}, {"source": 278, "target": 228, "value": 3}, {"source": 278, "target": 249, "value": 9}, {"source": 278, "target": 275, "value": 8}, {"source": 279, "target": 221, "value": 4}, {"source": 279, "target": 227, "value": 9}, {"source": 279, "target": 233, "value": 2}, {"source": 281, "target": 221, "value": 2}, {"source": 281, "target": 231, "value": 3}, {"source": 281, "target": 243, "value": 8}, {"source": 281, "target": 273, "value": 10}, {"source": 285, "target": 233, "value": 4}, {"source": 285, "target": 245, "value": 10}, {"source": 285, "target": 248, "value": 10}, {"source": 285, "target": 261, "value": 8}, {"source": 285, "target": 273, "value": 8}, {"source": 287, "target": 225, "value": 8}, {"source": 287, "target": 263, "value": 9}, {"source": 293, "target": 221, "value": 11}, {"source": 293, "target": 243, "value": 1}, {"source": 293, "target": 245, "value": 9}, {"source": 293, "target": 251, "value": 8}, {"source": 293, "target": 255, "value": 8}, {"source": 293, "target": 261, "value": 3}, {"source": 293, "target": 281, "value": 8}, {"source": 293, "target": 285, "value": 8}, {"source": 293, "target": 291, "value": 8}, {"source": 303, "target": 227, "value": 9}, {"source": 303, "target": 228, "value": 8}, {"source": 303, "target": 249, "value": 10}, {"source": 303, "target": 278, "value": 9}, {"source": 303, "target": 279, "value": 8}, {"source": 303, "target": 293, "value": 9}, {"source": 305, "target": 245, "value": 9}, {"source": 305, "target": 251, "value": 7}, {"source": 305, "target": 255, "value": 2}, {"source": 305, "target": 293, "value": 8}, {"source": 309, "target": 219, "value": 9}, {"source": 309, "target": 221, "value": 8}, {"source": 309, "target": 248, "value": 8}, {"source": 309, "target": 257, "value": 8}, {"source": 309, "target": 261, "value": 9}, {"source": 309, "target": 263, "value": 11}, {"source": 309, "target": 273, "value": 8}, {"source": 311, "target": 249, "value": 8}, {"source": 311, "target": 261, "value": 3}, {"source": 311, "target": 285, "value": 8}, {"source": 315, "target": 215, "value": 4}, {"source": 315, "target": 311, "value": 3}, {"source": 317, "target": 221, "value": 10}, {"source": 317, "target": 227, "value": 9}, {"source": 317, "target": 233, "value": 10}, {"source": 317, "target": 243, "value": 4}, {"source": 317, "target": 255, "value": 8}, {"source": 317, "target": 279, "value": 8}, {"source": 317, "target": 293, "value": 3}, {"source": 317, "target": 305, "value": 8}, {"source": 321, "target": 219, "value": 10}, {"source": 321, "target": 221, "value": 2}, {"source": 321, "target": 225, "value": 7}, {"source": 321, "target": 228, "value": 8}, {"source": 321, "target": 233, "value": 4}, {"source": 321, "target": 249, "value": 11}, {"source": 321, "target": 251, "value": 8}, {"source": 321, "target": 261, "value": 9}, {"source": 321, "target": 275, "value": 2}, {"source": 321, "target": 278, "value": 7}, {"source": 321, "target": 279, "value": 4}, {"source": 321, "target": 293, "value": 11}, {"source": 321, "target": 309, "value": 8}, {"source": 323, "target": 219, "value": 10}, {"source": 323, "target": 221, "value": 8}, {"source": 323, "target": 248, "value": 9}, {"source": 323, "target": 257, "value": 9}, {"source": 323, "target": 261, "value": 8}, {"source": 323, "target": 273, "value": 1}, {"source": 323, "target": 309, "value": 8}, {"source": 333, "target": 221, "value": 4}, {"source": 333, "target": 233, "value": 0}, {"source": 333, "target": 243, "value": 9}, {"source": 333, "target": 245, "value": 2}, {"source": 333, "target": 248, "value": 8}, {"source": 333, "target": 257, "value": 4}, {"source": 333, "target": 279, "value": 4}, {"source": 333, "target": 281, "value": 8}, {"source": 333, "target": 285, "value": 9}, {"source": 333, "target": 293, "value": 9}, {"source": 333, "target": 309, "value": 8}, {"source": 333, "target": 315, "value": 3}, {"source": 333, "target": 317, "value": 9}, {"source": 333, "target": 321, "value": 4}, {"source": 335, "target": 233, "value": 2}, {"source": 335, "target": 243, "value": 11}, {"source": 335, "target": 273, "value": 8}, {"source": 335, "target": 285, "value": 3}, {"source": 335, "target": 293, "value": 10}, {"source": 335, "target": 303, "value": 3}, {"source": 335, "target": 333, "value": 8}, {"source": 339, "target": 243, "value": 11}, {"source": 339, "target": 279, "value": 3}, {"source": 339, "target": 293, "value": 10}, {"source": 339, "target": 335, "value": 8}, {"source": 341, "target": 251, "value": 9}, {"source": 341, "target": 291, "value": 3}, {"source": 341, "target": 293, "value": 4}, {"source": 341, "target": 303, "value": 8}, {"source": 345, "target": 233, "value": 4}, {"source": 345, "target": 245, "value": 2}, {"source": 345, "target": 248, "value": 8}, {"source": 345, "target": 257, "value": 8}, {"source": 345, "target": 285, "value": 9}, {"source": 345, "target": 333, "value": 8}, {"source": 347, "target": 221, "value": 8}, {"source": 347, "target": 273, "value": 4}, {"source": 347, "target": 285, "value": 8}, {"source": 347, "target": 293, "value": 7}, {"source": 347, "target": 323, "value": 9}, {"source": 348, "target": 219, "value": 8}, {"source": 348, "target": 233, "value": 4}, {"source": 348, "target": 245, "value": 8}, {"source": 348, "target": 248, "value": 4}, {"source": 348, "target": 257, "value": 7}, {"source": 348, "target": 273, "value": 9}, {"source": 348, "target": 285, "value": 10}, {"source": 348, "target": 309, "value": 8}, {"source": 348, "target": 323, "value": 9}, {"source": 348, "target": 333, "value": 8}, {"source": 348, "target": 345, "value": 8}, {"source": 351, "target": 251, "value": 3}, {"source": 351, "target": 263, "value": 8}, {"source": 351, "target": 309, "value": 9}, {"source": 351, "target": 341, "value": 9}, {"source": 353, "target": 215, "value": 8}, {"source": 353, "target": 227, "value": 4}, {"source": 353, "target": 228, "value": 9}, {"source": 353, "target": 249, "value": 10}, {"source": 353, "target": 278, "value": 9}, {"source": 353, "target": 279, "value": 4}, {"source": 353, "target": 293, "value": 9}, {"source": 353, "target": 303, "value": 1}, {"source": 353, "target": 315, "value": 8}, {"source": 353, "target": 341, "value": 8}, {"source": 363, "target": 255, "value": 9}, {"source": 363, "target": 261, "value": 9}, {"source": 363, "target": 263, "value": 2}, {"source": 363, "target": 285, "value": 8}, {"source": 363, "target": 287, "value": 9}, {"source": 363, "target": 305, "value": 8}, {"source": 363, "target": 309, "value": 10}, {"source": 363, "target": 311, "value": 9}, {"source": 363, "target": 317, "value": 7}, {"source": 363, "target": 351, "value": 8}, {"source": 365, "target": 215, "value": 4}, {"source": 365, "target": 225, "value": 9}, {"source": 365, "target": 227, "value": 4}, {"source": 365, "target": 228, "value": 10}, {"source": 365, "target": 275, "value": 9}, {"source": 365, "target": 278, "value": 10}, {"source": 365, "target": 291, "value": 8}, {"source": 365, "target": 293, "value": 9}, {"source": 365, "target": 315, "value": 3}, {"source": 365, "target": 317, "value": 4}, {"source": 365, "target": 321, "value": 9}, {"source": 365, "target": 341, "value": 8}, {"source": 365, "target": 353, "value": 8}, {"source": 369, "target": 219, "value": 4}, {"source": 369, "target": 221, "value": 9}, {"source": 369, "target": 231, "value": 8}, {"source": 369, "target": 248, "value": 8}, {"source": 369, "target": 257, "value": 8}, {"source": 369, "target": 273, "value": 5}, {"source": 369, "target": 281, "value": 8}, {"source": 369, "target": 309, "value": 9}, {"source": 369, "target": 321, "value": 10}, {"source": 369, "target": 323, "value": 10}, {"source": 369, "target": 348, "value": 8}, {"source": 371, "target": 221, "value": 4}, {"source": 371, "target": 261, "value": 10}, {"source": 371, "target": 293, "value": 11}, {"source": 371, "target": 309, "value": 8}, {"source": 371, "target": 321, "value": 3}, {"source": 375, "target": 225, "value": 4}, {"source": 375, "target": 228, "value": 8}, {"source": 375, "target": 275, "value": 4}, {"source": 375, "target": 278, "value": 8}, {"source": 375, "target": 287, "value": 3}, {"source": 375, "target": 321, "value": 7}, {"source": 375, "target": 365, "value": 9}, {"source": 377, "target": 221, "value": 10}, {"source": 377, "target": 225, "value": 9}, {"source": 377, "target": 227, "value": 4}, {"source": 377, "target": 231, "value": 8}, {"source": 377, "target": 243, "value": 10}, {"source": 377, "target": 255, "value": 10}, {"source": 377, "target": 273, "value": 4}, {"source": 377, "target": 275, "value": 2}, {"source": 377, "target": 279, "value": 10}, {"source": 377, "target": 281, "value": 8}, {"source": 377, "target": 285, "value": 9}, {"source": 377, "target": 293, "value": 10}, {"source": 377, "target": 303, "value": 9}, {"source": 377, "target": 305, "value": 10}, {"source": 377, "target": 317, "value": 9}, {"source": 377, "target": 335, "value": 4}, {"source": 377, "target": 339, "value": 8}, {"source": 377, "target": 345, "value": 3}, {"source": 377, "target": 353, "value": 4}, {"source": 377, "target": 365, "value": 4}, {"source": 377, "target": 369, "value": 7}, {"source": 377, "target": 375, "value": 8}, {"source": 378, "target": 225, "value": 8}, {"source": 378, "target": 228, "value": 4}, {"source": 378, "target": 249, "value": 8}, {"source": 378, "target": 275, "value": 8}, {"source": 378, "target": 278, "value": 4}, {"source": 378, "target": 303, "value": 9}, {"source": 378, "target": 321, "value": 8}, {"source": 378, "target": 353, "value": 9}, {"source": 378, "target": 365, "value": 10}, {"source": 378, "target": 375, "value": 8}, {"source": 381, "target": 221, "value": 10}, {"source": 381, "target": 231, "value": 4}, {"source": 381, "target": 233, "value": 4}, {"source": 381, "target": 243, "value": 8}, {"source": 381, "target": 251, "value": 3}, {"source": 381, "target": 257, "value": 8}, {"source": 381, "target": 273, "value": 10}, {"source": 381, "target": 281, "value": 2}, {"source": 381, "target": 291, "value": 9}, {"source": 381, "target": 293, "value": 8}, {"source": 381, "target": 321, "value": 3}, {"source": 381, "target": 333, "value": 2}, {"source": 381, "target": 341, "value": 9}, {"source": 381, "target": 369, "value": 8}, {"source": 381, "target": 377, "value": 8}, {"source": 383, "target": 221, "value": 4}, {"source": 383, "target": 233, "value": 1}, {"source": 383, "target": 257, "value": 8}, {"source": 383, "target": 279, "value": 4}, {"source": 383, "target": 317, "value": 9}, {"source": 383, "target": 321, "value": 4}, {"source": 383, "target": 333, "value": 1}, {"source": 383, "target": 335, "value": 9}, {"source": 383, "target": 381, "value": 7}, {"source": 393, "target": 221, "value": 3}, {"source": 393, "target": 243, "value": 1}, {"source": 393, "target": 245, "value": 9}, {"source": 393, "target": 251, "value": 8}, {"source": 393, "target": 255, "value": 8}, {"source": 393, "target": 281, "value": 8}, {"source": 393, "target": 291, "value": 3}, {"source": 393, "target": 293, "value": 1}, {"source": 393, "target": 305, "value": 8}, {"source": 393, "target": 317, "value": 3}, {"source": 393, "target": 333, "value": 9}, {"source": 393, "target": 335, "value": 10}, {"source": 393, "target": 339, "value": 10}, {"source": 393, "target": 377, "value": 9}, {"source": 393, "target": 381, "value": 8}, {"source": 395, "target": 233, "value": 4}, {"source": 395, "target": 245, "value": 4}, {"source": 395, "target": 248, "value": 8}, {"source": 395, "target": 279, "value": 3}, {"source": 395, "target": 285, "value": 10}, {"source": 395, "target": 333, "value": 8}, {"source": 395, "target": 345, "value": 3}, {"source": 395, "target": 348, "value": 8}, {"source": 398, "target": 219, "value": 8}, {"source": 398, "target": 233, "value": 4}, {"source": 398, "target": 243, "value": 9}, {"source": 398, "target": 245, "value": 8}, {"source": 398, "target": 248, "value": 4}, {"source": 398, "target": 257, "value": 8}, {"source": 398, "target": 273, "value": 10}, {"source": 398, "target": 281, "value": 8}, {"source": 398, "target": 285, "value": 10}, {"source": 398, "target": 293, "value": 9}, {"source": 398, "target": 309, "value": 9}, {"source": 398, "target": 323, "value": 9}, {"source": 398, "target": 333, "value": 4}, {"source": 398, "target": 345, "value": 7}, {"source": 398, "target": 348, "value": 3}, {"source": 398, "target": 369, "value": 8}, {"source": 398, "target": 381, "value": 8}, {"source": 398, "target": 393, "value": 9}, {"source": 398, "target": 395, "value": 8}, {"source": 399, "target": 228, "value": 9}, {"source": 399, "target": 249, "value": 4}, {"source": 399, "target": 251, "value": 10}, {"source": 399, "target": 255, "value": 3}, {"source": 399, "target": 278, "value": 8}, {"source": 399, "target": 303, "value": 11}, {"source": 399, "target": 321, "value": 11}, {"source": 399, "target": 353, "value": 10}, {"source": 399, "target": 378, "value": 8}, {"source": 399, "target": 395, "value": 3}, {"source": 401, "target": 243, "value": 3}, {"source": 401, "target": 251, "value": 4}, {"source": 401, "target": 263, "value": 8}, {"source": 401, "target": 341, "value": 9}, {"source": 401, "target": 351, "value": 3}, {"source": 401, "target": 363, "value": 8}, {"source": 401, "target": 383, "value": 3}, {"source": 405, "target": 215, "value": 9}, {"source": 405, "target": 233, "value": 10}, {"source": 405, "target": 245, "value": 10}, {"source": 405, "target": 251, "value": 8}, {"source": 405, "target": 255, "value": 2}, {"source": 405, "target": 263, "value": 11}, {"source": 405, "target": 293, "value": 8}, {"source": 405, "target": 303, "value": 9}, {"source": 405, "target": 305, "value": 2}, {"source": 405, "target": 309, "value": 8}, {"source": 405, "target": 315, "value": 9}, {"source": 405, "target": 317, "value": 8}, {"source": 405, "target": 333, "value": 10}, {"source": 405, "target": 335, "value": 8}, {"source": 405, "target": 351, "value": 9}, {"source": 405, "target": 353, "value": 4}, {"source": 405, "target": 363, "value": 4}, {"source": 405, "target": 365, "value": 9}, {"source": 405, "target": 377, "value": 11}, {"source": 405, "target": 383, "value": 10}, {"source": 405, "target": 393, "value": 8}, {"source": 407, "target": 233, "value": 4}, {"source": 407, "target": 245, "value": 8}, {"source": 407, "target": 257, "value": 4}, {"source": 407, "target": 333, "value": 4}, {"source": 407, "target": 345, "value": 7}, {"source": 407, "target": 381, "value": 8}, {"source": 407, "target": 383, "value": 9}, {"source": 411, "target": 249, "value": 8}, {"source": 411, "target": 261, "value": 4}, {"source": 411, "target": 285, "value": 8}, {"source": 411, "target": 311, "value": 4}, {"source": 411, "target": 363, "value": 9}, {"source": 413, "target": 215, "value": 26}, {"source": 413, "target": 219, "value": 27}, {"source": 413, "target": 221, "value": 26}, {"source": 413, "target": 225, "value": 6}, {"source": 413, "target": 227, "value": 28}, {"source": 413, "target": 228, "value": 6}, {"source": 413, "target": 231, "value": 27}, {"source": 413, "target": 233, "value": 27}, {"source": 413, "target": 243, "value": 29}, {"source": 413, "target": 245, "value": 28}, {"source": 413, "target": 248, "value": 28}, {"source": 413, "target": 249, "value": 29}, {"source": 413, "target": 251, "value": 28}, {"source": 413, "target": 255, "value": 28}, {"source": 413, "target": 257, "value": 29}, {"source": 413, "target": 261, "value": 6}, {"source": 413, "target": 263, "value": 1}, {"source": 413, "target": 273, "value": 7}, {"source": 413, "target": 275, "value": 6}, {"source": 413, "target": 278, "value": 6}, {"source": 413, "target": 279, "value": 29}, {"source": 413, "target": 287, "value": 4}, {"source": 413, "target": 309, "value": 8}, {"source": 413, "target": 321, "value": 7}, {"source": 413, "target": 323, "value": 8}, {"source": 413, "target": 339, "value": 7}, {"source": 413, "target": 351, "value": 7}, {"source": 413, "target": 353, "value": 3}, {"source": 413, "target": 363, "value": 1}, {"source": 413, "target": 365, "value": 8}, {"source": 413, "target": 375, "value": 8}, {"source": 413, "target": 378, "value": 8}, {"source": 413, "target": 401, "value": 8}, {"source": 413, "target": 405, "value": 9}, {"source": 423, "target": 219, "value": 10}, {"source": 423, "target": 221, "value": 8}, {"source": 423, "target": 248, "value": 8}, {"source": 423, "target": 257, "value": 9}, {"source": 423, "target": 261, "value": 8}, {"source": 423, "target": 273, "value": 1}, {"source": 423, "target": 309, "value": 8}, {"source": 423, "target": 323, "value": 2}, {"source": 423, "target": 347, "value": 8}, {"source": 423, "target": 348, "value": 8}, {"source": 423, "target": 369, "value": 10}, {"source": 423, "target": 398, "value": 9}, {"source": 423, "target": 413, "value": 8}, {"source": 425, "target": 225, "value": 2}, {"source": 425, "target": 228, "value": 8}, {"source": 425, "target": 275, "value": 4}, {"source": 425, "target": 278, "value": 8}, {"source": 425, "target": 287, "value": 8}, {"source": 425, "target": 321, "value": 8}, {"source": 425, "target": 365, "value": 9}, {"source": 425, "target": 375, "value": 3}, {"source": 425, "target": 377, "value": 9}, {"source": 425, "target": 378, "value": 8}, {"source": 425, "target": 413, "value": 8}, {"source": 428, "target": 225, "value": 8}, {"source": 428, "target": 228, "value": 4}, {"source": 428, "target": 249, "value": 8}, {"source": 428, "target": 275, "value": 8}, {"source": 428, "target": 278, "value": 4}, {"source": 428, "target": 303, "value": 9}, {"source": 428, "target": 321, "value": 8}, {"source": 428, "target": 353, "value": 9}, {"source": 428, "target": 365, "value": 9}, {"source": 428, "target": 375, "value": 7}, {"source": 428, "target": 378, "value": 3}, {"source": 428, "target": 399, "value": 8}, {"source": 428, "target": 413, "value": 8}, {"source": 428, "target": 425, "value": 8}, {"source": 429, "target": 221, "value": 11}, {"source": 429, "target": 225, "value": 8}, {"source": 429, "target": 233, "value": 11}, {"source": 429, "target": 243, "value": 9}, {"source": 429, "target": 255, "value": 8}, {"source": 429, "target": 279, "value": 4}, {"source": 429, "target": 287, "value": 9}, {"source": 429, "target": 291, "value": 8}, {"source": 429, "target": 293, "value": 8}, {"source": 429, "target": 305, "value": 8}, {"source": 429, "target": 317, "value": 8}, {"source": 429, "target": 333, "value": 11}, {"source": 429, "target": 335, "value": 9}, {"source": 429, "target": 339, "value": 9}, {"source": 429, "target": 341, "value": 8}, {"source": 429, "target": 377, "value": 4}, {"source": 429, "target": 381, "value": 9}, {"source": 429, "target": 383, "value": 10}, {"source": 429, "target": 393, "value": 8}, {"source": 429, "target": 405, "value": 8}, {"source": 429, "target": 425, "value": 9}, {"source": 431, "target": 221, "value": 10}, {"source": 431, "target": 231, "value": 4}, {"source": 431, "target": 243, "value": 9}, {"source": 431, "target": 273, "value": 10}, {"source": 431, "target": 281, "value": 2}, {"source": 431, "target": 293, "value": 9}, {"source": 431, "target": 333, "value": 8}, {"source": 431, "target": 369, "value": 7}, {"source": 431, "target": 377, "value": 7}, {"source": 431, "target": 381, "value": 2}, {"source": 431, "target": 393, "value": 9}, {"source": 431, "target": 398, "value": 8}, {"source": 435, "target": 263, "value": 3}, {"source": 435, "target": 273, "value": 8}, {"source": 435, "target": 285, "value": 4}, {"source": 435, "target": 333, "value": 3}, {"source": 435, "target": 335, "value": 4}, {"source": 435, "target": 377, "value": 9}, {"source": 437, "target": 225, "value": 8}, {"source": 437, "target": 228, "value": 8}, {"source": 437, "target": 249, "value": 8}, {"source": 437, "target": 251, "value": 3}, {"source": 437, "target": 263, "value": 9}, {"source": 437, "target": 278, "value": 8}, {"source": 437, "target": 285, "value": 8}, {"source": 437, "target": 287, "value": 4}, {"source": 437, "target": 293, "value": 9}, {"source": 437, "target": 303, "value": 10}, {"source": 437, "target": 321, "value": 3}, {"source": 437, "target": 347, "value": 9}, {"source": 437, "target": 353, "value": 10}, {"source": 437, "target": 363, "value": 9}, {"source": 437, "target": 378, "value": 8}, {"source": 437, "target": 399, "value": 8}, {"source": 437, "target": 413, "value": 4}, {"source": 437, "target": 425, "value": 8}, {"source": 437, "target": 428, "value": 7}, {"source": 437, "target": 429, "value": 9}, {"source": 441, "target": 291, "value": 4}, {"source": 441, "target": 293, "value": 5}, {"source": 441, "target": 303, "value": 8}, {"source": 441, "target": 341, "value": 2}, {"source": 441, "target": 353, "value": 8}, {"source": 441, "target": 365, "value": 8}, {"source": 441, "target": 381, "value": 9}, {"source": 441, "target": 429, "value": 8}, {"source": 441, "target": 437, "value": 3}, {"source": 443, "target": 243, "value": 2}, {"source": 443, "target": 281, "value": 8}, {"source": 443, "target": 293, "value": 1}, {"source": 443, "target": 317, "value": 4}, {"source": 443, "target": 333, "value": 9}, {"source": 443, "target": 335, "value": 10}, {"source": 443, "target": 339, "value": 10}, {"source": 443, "target": 377, "value": 9}, {"source": 443, "target": 381, "value": 8}, {"source": 443, "target": 393, "value": 1}, {"source": 443, "target": 398, "value": 9}, {"source": 443, "target": 429, "value": 8}, {"source": 443, "target": 431, "value": 9}, {"source": 453, "target": 215, "value": 4}, {"source": 453, "target": 227, "value": 4}, {"source": 453, "target": 228, "value": 8}, {"source": 453, "target": 249, "value": 10}, {"source": 453, "target": 263, "value": 9}, {"source": 453, "target": 278, "value": 8}, {"source": 453, "target": 279, "value": 4}, {"source": 453, "target": 293, "value": 10}, {"source": 453, "target": 303, "value": 1}, {"source": 453, "target": 315, "value": 4}, {"source": 453, "target": 341, "value": 8}, {"source": 453, "target": 353, "value": 0}, {"source": 453, "target": 363, "value": 9}, {"source": 453, "target": 365, "value": 4}, {"source": 453, "target": 377, "value": 4}, {"source": 453, "target": 378, "value": 8}, {"source": 453, "target": 399, "value": 9}, {"source": 453, "target": 401, "value": 8}, {"source": 453, "target": 405, "value": 3}, {"source": 453, "target": 407, "value": 3}, {"source": 453, "target": 413, "value": 9}, {"source": 453, "target": 428, "value": 8}, {"source": 453, "target": 437, "value": 9}, {"source": 453, "target": 441, "value": 8}, {"source": 455, "target": 245, "value": 10}, {"source": 455, "target": 251, "value": 8}, {"source": 455, "target": 255, "value": 1}, {"source": 455, "target": 263, "value": 9}, {"source": 455, "target": 293, "value": 8}, {"source": 455, "target": 305, "value": 4}, {"source": 455, "target": 363, "value": 9}, {"source": 455, "target": 377, "value": 11}, {"source": 455, "target": 393, "value": 8}, {"source": 455, "target": 395, "value": 3}, {"source": 455, "target": 401, "value": 8}, {"source": 455, "target": 405, "value": 3}, {"source": 455, "target": 413, "value": 9}, {"source": 455, "target": 429, "value": 8}, {"source": 455, "target": 453, "value": 7}, {"source": 459, "target": 263, "value": 11}, {"source": 459, "target": 309, "value": 4}, {"source": 459, "target": 333, "value": 8}, {"source": 459, "target": 351, "value": 10}, {"source": 459, "target": 363, "value": 10}, {"source": 459, "target": 371, "value": 3}, {"source": 459, "target": 405, "value": 7}, {"source": 459, "target": 413, "value": 10}, {"source": 459, "target": 441, "value": 3}, {"source": 461, "target": 215, "value": 8}, {"source": 461, "target": 221, "value": 9}, {"source": 461, "target": 249, "value": 8}, {"source": 461, "target": 261, "value": 2}, {"source": 461, "target": 263, "value": 8}, {"source": 461, "target": 273, "value": 8}, {"source": 461, "target": 285, "value": 8}, {"source": 461, "target": 287, "value": 8}, {"source": 461, "target": 309, "value": 4}, {"source": 461, "target": 311, "value": 4}, {"source": 461, "target": 315, "value": 8}, {"source": 461, "target": 321, "value": 9}, {"source": 461, "target": 323, "value": 8}, {"source": 461, "target": 333, "value": 8}, {"source": 461, "target": 339, "value": 10}, {"source": 461, "target": 353, "value": 8}, {"source": 461, "target": 363, "value": 4}, {"source": 461, "target": 365, "value": 7}, {"source": 461, "target": 371, "value": 9}, {"source": 461, "target": 405, "value": 9}, {"source": 461, "target": 411, "value": 3}, {"source": 461, "target": 413, "value": 2}, {"source": 461, "target": 423, "value": 8}, {"source": 461, "target": 429, "value": 3}, {"source": 461, "target": 437, "value": 8}, {"source": 461, "target": 453, "value": 4}, {"source": 461, "target": 459, "value": 8}, {"source": 465, "target": 215, "value": 4}, {"source": 465, "target": 227, "value": 8}, {"source": 465, "target": 315, "value": 4}, {"source": 465, "target": 317, "value": 9}, {"source": 465, "target": 353, "value": 8}, {"source": 465, "target": 365, "value": 2}, {"source": 465, "target": 377, "value": 8}, {"source": 465, "target": 405, "value": 9}, {"source": 465, "target": 453, "value": 4}, {"source": 465, "target": 461, "value": 7}, {"source": 467, "target": 243, "value": 9}, {"source": 467, "target": 255, "value": 10}, {"source": 467, "target": 293, "value": 4}, {"source": 467, "target": 305, "value": 8}, {"source": 467, "target": 317, "value": 4}, {"source": 467, "target": 363, "value": 8}, {"source": 467, "target": 393, "value": 4}, {"source": 467, "target": 405, "value": 7}, {"source": 467, "target": 443, "value": 9}, {"source": 471, "target": 221, "value": 4}, {"source": 471, "target": 261, "value": 10}, {"source": 471, "target": 293, "value": 11}, {"source": 471, "target": 309, "value": 8}, {"source": 471, "target": 321, "value": 4}, {"source": 471, "target": 371, "value": 4}, {"source": 471, "target": 461, "value": 8}, {"source": 473, "target": 219, "value": 9}, {"source": 473, "target": 221, "value": 4}, {"source": 473, "target": 248, "value": 8}, {"source": 473, "target": 257, "value": 9}, {"source": 473, "target": 261, "value": 8}, {"source": 473, "target": 273, "value": 1}, {"source": 473, "target": 285, "value": 8}, {"source": 473, "target": 309, "value": 8}, {"source": 473, "target": 323, "value": 1}, {"source": 473, "target": 335, "value": 8}, {"source": 473, "target": 347, "value": 4}, {"source": 473, "target": 348, "value": 8}, {"source": 473, "target": 369, "value": 10}, {"source": 473, "target": 377, "value": 9}, {"source": 473, "target": 398, "value": 8}, {"source": 473, "target": 399, "value": 3}, {"source": 473, "target": 413, "value": 9}, {"source": 473, "target": 423, "value": 1}, {"source": 473, "target": 435, "value": 8}, {"source": 473, "target": 461, "value": 8}, {"source": 483, "target": 221, "value": 4}, {"source": 483, "target": 233, "value": 1}, {"source": 483, "target": 257, "value": 8}, {"source": 483, "target": 279, "value": 4}, {"source": 483, "target": 317, "value": 9}, {"source": 483, "target": 321, "value": 4}, {"source": 483, "target": 333, "value": 1}, {"source": 483, "target": 335, "value": 10}, {"source": 483, "target": 381, "value": 8}, {"source": 483, "target": 383, "value": 2}, {"source": 483, "target": 405, "value": 10}, {"source": 483, "target": 407, "value": 8}, {"source": 483, "target": 429, "value": 9}, {"source": 485, "target": 233, "value": 4}, {"source": 485, "target": 245, "value": 9}, {"source": 485, "target": 248, "value": 9}, {"source": 485, "target": 273, "value": 8}, {"source": 485, "target": 285, "value": 2}, {"source": 485, "target": 293, "value": 8}, {"source": 485, "target": 333, "value": 8}, {"source": 485, "target": 335, "value": 4}, {"source": 485, "target": 345, "value": 9}, {"source": 485, "target": 347, "value": 8}, {"source": 485, "target": 348, "value": 9}, {"source": 485, "target": 377, "value": 10}, {"source": 485, "target": 395, "value": 9}, {"source": 485, "target": 398, "value": 9}, {"source": 485, "target": 435, "value": 3}, {"source": 485, "target": 437, "value": 9}, {"source": 485, "target": 473, "value": 8}, {"source": 489, "target": 243, "value": 3}, {"source": 489, "target": 251, "value": 8}, {"source": 489, "target": 263, "value": 8}, {"source": 489, "target": 281, "value": 8}, {"source": 489, "target": 287, "value": 9}, {"source": 489, "target": 293, "value": 2}, {"source": 489, "target": 317, "value": 10}, {"source": 489, "target": 333, "value": 10}, {"source": 489, "target": 335, "value": 8}, {"source": 489, "target": 339, "value": 4}, {"source": 489, "target": 341, "value": 9}, {"source": 489, "target": 351, "value": 8}, {"source": 489, "target": 363, "value": 8}, {"source": 489, "target": 377, "value": 8}, {"source": 489, "target": 381, "value": 8}, {"source": 489, "target": 393, "value": 2}, {"source": 489, "target": 398, "value": 9}, {"source": 489, "target": 401, "value": 8}, {"source": 489, "target": 413, "value": 2}, {"source": 489, "target": 429, "value": 9}, {"source": 489, "target": 431, "value": 8}, {"source": 489, "target": 437, "value": 8}, {"source": 489, "target": 443, "value": 3}, {"source": 489, "target": 461, "value": 4}, {"source": 489, "target": 467, "value": 10}, {"source": 491, "target": 291, "value": 4}, {"source": 491, "target": 293, "value": 10}, {"source": 491, "target": 341, "value": 4}, {"source": 491, "target": 365, "value": 8}, {"source": 491, "target": 381, "value": 9}, {"source": 491, "target": 429, "value": 7}, {"source": 491, "target": 441, "value": 3}, {"source": 495, "target": 233, "value": 4}, {"source": 495, "target": 245, "value": 4}, {"source": 495, "target": 248, "value": 8}, {"source": 495, "target": 285, "value": 10}, {"source": 495, "target": 309, "value": 3}, {"source": 495, "target": 333, "value": 8}, {"source": 495, "target": 345, "value": 4}, {"source": 495, "target": 348, "value": 8}, {"source": 495, "target": 395, "value": 4}, {"source": 495, "target": 398, "value": 8}, {"source": 495, "target": 485, "value": 8}, {"source": 497, "target": 221, "value": 8}, {"source": 497, "target": 263, "value": 5}, {"source": 497, "target": 273, "value": 4}, {"source": 497, "target": 285, "value": 8}, {"source": 497, "target": 293, "value": 8}, {"source": 497, "target": 309, "value": 4}, {"source": 497, "target": 323, "value": 9}, {"source": 497, "target": 347, "value": 4}, {"source": 497, "target": 351, "value": 4}, {"source": 497, "target": 363, "value": 4}, {"source": 497, "target": 405, "value": 4}, {"source": 497, "target": 413, "value": 4}, {"source": 497, "target": 423, "value": 9}, {"source": 497, "target": 437, "value": 2}, {"source": 497, "target": 459, "value": 4}, {"source": 497, "target": 473, "value": 4}, {"source": 497, "target": 485, "value": 8}, {"source": 498, "target": 219, "value": 8}, {"source": 498, "target": 233, "value": 4}, {"source": 498, "target": 245, "value": 8}, {"source": 498, "target": 248, "value": 4}, {"source": 498, "target": 257, "value": 8}, {"source": 498, "target": 273, "value": 10}, {"source": 498, "target": 285, "value": 10}, {"source": 498, "target": 309, "value": 9}, {"source": 498, "target": 323, "value": 10}, {"source": 498, "target": 333, "value": 9}, {"source": 498, "target": 345, "value": 8}, {"source": 498, "target": 348, "value": 4}, {"source": 498, "target": 369, "value": 8}, {"source": 498, "target": 395, "value": 8}, {"source": 498, "target": 398, "value": 4}, {"source": 498, "target": 423, "value": 9}, {"source": 498, "target": 473, "value": 9}, {"source": 498, "target": 485, "value": 9}, {"source": 498, "target": 495, "value": 8}, {"source": 501, "target": 249, "value": 8}, {"source": 501, "target": 251, "value": 4}, {"source": 501, "target": 261, "value": 9}, {"source": 501, "target": 263, "value": 8}, {"source": 501, "target": 311, "value": 9}, {"source": 501, "target": 341, "value": 10}, {"source": 501, "target": 351, "value": 4}, {"source": 501, "target": 363, "value": 8}, {"source": 501, "target": 401, "value": 2}, {"source": 501, "target": 411, "value": 9}, {"source": 501, "target": 413, "value": 2}, {"source": 501, "target": 453, "value": 8}, {"source": 501, "target": 455, "value": 8}, {"source": 501, "target": 461, "value": 9}, {"source": 501, "target": 483, "value": 3}, {"source": 501, "target": 489, "value": 8}, {"source": 503, "target": 227, "value": 8}, {"source": 503, "target": 228, "value": 8}, {"source": 503, "target": 245, "value": 8}, {"source": 503, "target": 249, "value": 9}, {"source": 503, "target": 251, "value": 9}, {"source": 503, "target": 257, "value": 8}, {"source": 503, "target": 278, "value": 8}, {"source": 503, "target": 279, "value": 9}, {"source": 503, "target": 293, "value": 10}, {"source": 503, "target": 303, "value": 2}, {"source": 503, "target": 341, "value": 8}, {"source": 503, "target": 345, "value": 8}, {"source": 503, "target": 351, "value": 9}, {"source": 503, "target": 353, "value": 1}, {"source": 503, "target": 377, "value": 8}, {"source": 503, "target": 378, "value": 8}, {"source": 503, "target": 399, "value": 9}, {"source": 503, "target": 401, "value": 9}, {"source": 503, "target": 405, "value": 9}, {"source": 503, "target": 407, "value": 7}, {"source": 503, "target": 428, "value": 8}, {"source": 503, "target": 437, "value": 9}, {"source": 503, "target": 441, "value": 8}, {"source": 503, "target": 453, "value": 1}, {"source": 503, "target": 501, "value": 8}, {"source": 513, "target": 215, "value": 26}, {"source": 513, "target": 219, "value": 27}, {"source": 513, "target": 221, "value": 26}, {"source": 513, "target": 225, "value": 6}, {"source": 513, "target": 227, "value": 29}, {"source": 513, "target": 228, "value": 6}, {"source": 513, "target": 231, "value": 28}, {"source": 513, "target": 233, "value": 29}, {"source": 513, "target": 243, "value": 29}, {"source": 513, "target": 245, "value": 28}, {"source": 513, "target": 248, "value": 29}, {"source": 513, "target": 249, "value": 29}, {"source": 513, "target": 251, "value": 28}, {"source": 513, "target": 255, "value": 28}, {"source": 513, "target": 257, "value": 29}, {"source": 513, "target": 261, "value": 29}, {"source": 513, "target": 263, "value": 1}, {"source": 513, "target": 273, "value": 28}, {"source": 513, "target": 275, "value": 6}, {"source": 513, "target": 278, "value": 6}, {"source": 513, "target": 287, "value": 3}, {"source": 513, "target": 309, "value": 8}, {"source": 513, "target": 321, "value": 7}, {"source": 513, "target": 351, "value": 7}, {"source": 513, "target": 363, "value": 1}, {"source": 513, "target": 365, "value": 9}, {"source": 513, "target": 375, "value": 8}, {"source": 513, "target": 378, "value": 8}, {"source": 513, "target": 383, "value": 3}, {"source": 513, "target": 401, "value": 8}, {"source": 513, "target": 405, "value": 9}, {"source": 513, "target": 413, "value": 0}, {"source": 513, "target": 425, "value": 8}, {"source": 513, "target": 428, "value": 8}, {"source": 513, "target": 437, "value": 4}, {"source": 513, "target": 453, "value": 2}, {"source": 513, "target": 455, "value": 9}, {"source": 513, "target": 459, "value": 9}, {"source": 513, "target": 461, "value": 4}, {"source": 513, "target": 489, "value": 4}, {"source": 513, "target": 497, "value": 4}, {"source": 513, "target": 501, "value": 8}, {"source": 515, "target": 215, "value": 4}, {"source": 515, "target": 315, "value": 4}, {"source": 515, "target": 353, "value": 8}, {"source": 515, "target": 365, "value": 4}, {"source": 515, "target": 371, "value": 3}, {"source": 515, "target": 405, "value": 9}, {"source": 515, "target": 441, "value": 3}, {"source": 515, "target": 453, "value": 4}, {"source": 515, "target": 461, "value": 7}, {"source": 515, "target": 465, "value": 3}, {"source": 519, "target": 219, "value": 4}, {"source": 519, "target": 221, "value": 9}, {"source": 519, "target": 231, "value": 8}, {"source": 519, "target": 248, "value": 9}, {"source": 519, "target": 257, "value": 8}, {"source": 519, "target": 273, "value": 5}, {"source": 519, "target": 281, "value": 8}, {"source": 519, "target": 309, "value": 10}, {"source": 519, "target": 321, "value": 10}, {"source": 519, "target": 323, "value": 11}, {"source": 519, "target": 347, "value": 3}, {"source": 519, "target": 348, "value": 9}, {"source": 519, "target": 369, "value": 2}, {"source": 519, "target": 377, "value": 8}, {"source": 519, "target": 381, "value": 8}, {"source": 519, "target": 398, "value": 8}, {"source": 519, "target": 423, "value": 10}, {"source": 519, "target": 431, "value": 8}, {"source": 519, "target": 473, "value": 10}, {"source": 519, "target": 498, "value": 8}, {"source": 521, "target": 221, "value": 2}, {"source": 521, "target": 233, "value": 8}, {"source": 521, "target": 261, "value": 10}, {"source": 521, "target": 279, "value": 8}, {"source": 521, "target": 293, "value": 11}, {"source": 521, "target": 309, "value": 8}, {"source": 521, "target": 321, "value": 2}, {"source": 521, "target": 333, "value": 8}, {"source": 521, "target": 335, "value": 3}, {"source": 521, "target": 371, "value": 4}, {"source": 521, "target": 383, "value": 8}, {"source": 521, "target": 405, "value": 3}, {"source": 521, "target": 461, "value": 9}, {"source": 521, "target": 471, "value": 3}, {"source": 521, "target": 483, "value": 8}, {"source": 525, "target": 225, "value": 2}, {"source": 525, "target": 228, "value": 8}, {"source": 525, "target": 251, "value": 8}, {"source": 525, "target": 273, "value": 8}, {"source": 525, "target": 275, "value": 4}, {"source": 525, "target": 278, "value": 8}, {"source": 525, "target": 285, "value": 9}, {"source": 525, "target": 287, "value": 8}, {"source": 525, "target": 321, "value": 8}, {"source": 525, "target": 335, "value": 9}, {"source": 525, "target": 351, "value": 8}, {"source": 525, "target": 365, "value": 10}, {"source": 525, "target": 375, "value": 4}, {"source": 525, "target": 377, "value": 4}, {"source": 525, "target": 378, "value": 8}, {"source": 525, "target": 401, "value": 8}, {"source": 525, "target": 413, "value": 8}, {"source": 525, "target": 425, "value": 2}, {"source": 525, "target": 428, "value": 8}, {"source": 525, "target": 429, "value": 9}, {"source": 525, "target": 435, "value": 9}, {"source": 525, "target": 437, "value": 8}, {"source": 525, "target": 473, "value": 8}, {"source": 525, "target": 485, "value": 9}, {"source": 525, "target": 501, "value": 8}, {"source": 525, "target": 503, "value": 8}, {"source": 525, "target": 513, "value": 8}, {"source": 527, "target": 227, "value": 4}, {"source": 527, "target": 279, "value": 10}, {"source": 527, "target": 303, "value": 9}, {"source": 527, "target": 317, "value": 10}, {"source": 527, "target": 353, "value": 4}, {"source": 527, "target": 365, "value": 4}, {"source": 527, "target": 377, "value": 4}, {"source": 527, "target": 453, "value": 4}, {"source": 527, "target": 465, "value": 7}, {"source": 527, "target": 503, "value": 8}, {"source": 528, "target": 225, "value": 8}, {"source": 528, "target": 228, "value": 4}, {"source": 528, "target": 249, "value": 8}, {"source": 528, "target": 275, "value": 8}, {"source": 528, "target": 278, "value": 4}, {"source": 528, "target": 303, "value": 10}, {"source": 528, "target": 321, "value": 8}, {"source": 528, "target": 353, "value": 10}, {"source": 528, "target": 365, "value": 10}, {"source": 528, "target": 375, "value": 8}, {"source": 528, "target": 378, "value": 4}, {"source": 528, "target": 399, "value": 8}, {"source": 528, "target": 413, "value": 8}, {"source": 528, "target": 425, "value": 8}, {"source": 528, "target": 428, "value": 4}, {"source": 528, "target": 437, "value": 7}, {"source": 528, "target": 453, "value": 9}, {"source": 528, "target": 503, "value": 8}, {"source": 528, "target": 513, "value": 8}, {"source": 528, "target": 525, "value": 8}, {"source": 531, "target": 221, "value": 10}, {"source": 531, "target": 231, "value": 4}, {"source": 531, "target": 273, "value": 4}, {"source": 531, "target": 279, "value": 9}, {"source": 531, "target": 281, "value": 4}, {"source": 531, "target": 285, "value": 8}, {"source": 531, "target": 335, "value": 8}, {"source": 531, "target": 369, "value": 8}, {"source": 531, "target": 377, "value": 4}, {"source": 531, "target": 381, "value": 4}, {"source": 531, "target": 429, "value": 9}, {"source": 531, "target": 431, "value": 4}, {"source": 531, "target": 435, "value": 7}, {"source": 531, "target": 473, "value": 8}, {"source": 531, "target": 485, "value": 7}, {"source": 531, "target": 519, "value": 8}, {"source": 531, "target": 525, "value": 9}, {"source": 533, "target": 221, "value": 4}, {"source": 533, "target": 233, "value": 0}, {"source": 533, "target": 243, "value": 9}, {"source": 533, "target": 245, "value": 8}, {"source": 533, "target": 248, "value": 8}, {"source": 533, "target": 257, "value": 4}, {"source": 533, "target": 279, "value": 4}, {"source": 533, "target": 281, "value": 8}, {"source": 533, "target": 285, "value": 9}, {"source": 533, "target": 291, "value": 9}, {"source": 533, "target": 293, "value": 9}, {"source": 533, "target": 317, "value": 8}, {"source": 533, "target": 321, "value": 4}, {"source": 533, "target": 333, "value": 0}, {"source": 533, "target": 335, "value": 10}, {"source": 533, "target": 341, "value": 9}, {"source": 533, "target": 345, "value": 8}, {"source": 533, "target": 348, "value": 8}, {"source": 533, "target": 375, "value": 3}, {"source": 533, "target": 381, "value": 2}, {"source": 533, "target": 383, "value": 1}, {"source": 533, "target": 393, "value": 9}, {"source": 533, "target": 395, "value": 8}, {"source": 533, "target": 398, "value": 4}, {"source": 533, "target": 405, "value": 11}, {"source": 533, "target": 407, "value": 4}, {"source": 533, "target": 429, "value": 4}, {"source": 533, "target": 431, "value": 8}, {"source": 533, "target": 441, "value": 9}, {"source": 533, "target": 443, "value": 9}, {"source": 533, "target": 483, "value": 1}, {"source": 533, "target": 485, "value": 8}, {"source": 533, "target": 489, "value": 8}, {"source": 533, "target": 491, "value": 9}, {"source": 533, "target": 495, "value": 8}, {"source": 533, "target": 498, "value": 8}, {"source": 533, "target": 515, "value": 3}, {"source": 533, "target": 521, "value": 8}, {"source": 543, "target": 243, "value": 2}, {"source": 543, "target": 281, "value": 8}, {"source": 543, "target": 293, "value": 1}, {"source": 543, "target": 317, "value": 4}, {"source": 543, "target": 333, "value": 10}, {"source": 543, "target": 335, "value": 9}, {"source": 543, "target": 339, "value": 9}, {"source": 543, "target": 377, "value": 8}, {"source": 543, "target": 381, "value": 8}, {"source": 543, "target": 393, "value": 1}, {"source": 543, "target": 398, "value": 9}, {"source": 543, "target": 429, "value": 7}, {"source": 543, "target": 431, "value": 9}, {"source": 543, "target": 443, "value": 2}, {"source": 543, "target": 467, "value": 8}, {"source": 543, "target": 489, "value": 2}, {"source": 543, "target": 533, "value": 8}, {"source": 545, "target": 228, "value": 9}, {"source": 545, "target": 233, "value": 4}, {"source": 545, "target": 243, "value": 9}, {"source": 545, "target": 245, "value": 2}, {"source": 545, "target": 248, "value": 8}, {"source": 545, "target": 249, "value": 8}, {"source": 545, "target": 257, "value": 8}, {"source": 545, "target": 273, "value": 10}, {"source": 545, "target": 278, "value": 9}, {"source": 545, "target": 285, "value": 10}, {"source": 545, "target": 293, "value": 9}, {"source": 545, "target": 303, "value": 11}, {"source": 545, "target": 317, "value": 8}, {"source": 545, "target": 323, "value": 10}, {"source": 545, "target": 333, "value": 8}, {"source": 545, "target": 345, "value": 2}, {"source": 545, "target": 348, "value": 8}, {"source": 545, "target": 353, "value": 11}, {"source": 545, "target": 378, "value": 8}, {"source": 545, "target": 393, "value": 9}, {"source": 545, "target": 395, "value": 4}, {"source": 545, "target": 398, "value": 8}, {"source": 545, "target": 399, "value": 8}, {"source": 545, "target": 407, "value": 8}, {"source": 545, "target": 423, "value": 9}, {"source": 545, "target": 428, "value": 8}, {"source": 545, "target": 437, "value": 8}, {"source": 545, "target": 443, "value": 9}, {"source": 545, "target": 453, "value": 9}, {"source": 545, "target": 473, "value": 9}, {"source": 545, "target": 485, "value": 9}, {"source": 545, "target": 495, "value": 3}, {"source": 545, "target": 498, "value": 8}, {"source": 545, "target": 503, "value": 4}, {"source": 545, "target": 528, "value": 8}, {"source": 545, "target": 533, "value": 8}, {"source": 545, "target": 543, "value": 8}, {"source": 548, "target": 219, "value": 8}, {"source": 548, "target": 233, "value": 4}, {"source": 548, "target": 245, "value": 8}, {"source": 548, "target": 248, "value": 4}, {"source": 548, "target": 257, "value": 8}, {"source": 548, "target": 273, "value": 10}, {"source": 548, "target": 285, "value": 10}, {"source": 548, "target": 309, "value": 9}, {"source": 548, "target": 323, "value": 10}, {"source": 548, "target": 333, "value": 9}, {"source": 548, "target": 345, "value": 8}, {"source": 548, "target": 348, "value": 4}, {"source": 548, "target": 369, "value": 8}, {"source": 548, "target": 395, "value": 8}, {"source": 548, "target": 398, "value": 4}, {"source": 548, "target": 423, "value": 9}, {"source": 548, "target": 473, "value": 9}, {"source": 548, "target": 485, "value": 9}, {"source": 548, "target": 495, "value": 7}, {"source": 548, "target": 498, "value": 3}, {"source": 548, "target": 519, "value": 8}, {"source": 548, "target": 533, "value": 8}, {"source": 548, "target": 545, "value": 8}, {"source": 549, "target": 228, "value": 9}, {"source": 549, "target": 249, "value": 2}, {"source": 549, "target": 251, "value": 10}, {"source": 549, "target": 261, "value": 8}, {"source": 549, "target": 263, "value": 9}, {"source": 549, "target": 278, "value": 9}, {"source": 549, "target": 303, "value": 11}, {"source": 549, "target": 309, "value": 9}, {"source": 549, "target": 311, "value": 8}, {"source": 549, "target": 321, "value": 11}, {"source": 549, "target": 351, "value": 8}, {"source": 549, "target": 353, "value": 10}, {"source": 549, "target": 363, "value": 9}, {"source": 549, "target": 378, "value": 8}, {"source": 549, "target": 399, "value": 4}, {"source": 549, "target": 405, "value": 9}, {"source": 549, "target": 411, "value": 8}, {"source": 549, "target": 413, "value": 8}, {"source": 549, "target": 428, "value": 8}, {"source": 549, "target": 437, "value": 8}, {"source": 549, "target": 453, "value": 10}, {"source": 549, "target": 459, "value": 8}, {"source": 549, "target": 461, "value": 8}, {"source": 549, "target": 497, "value": 4}, {"source": 549, "target": 501, "value": 8}, {"source": 549, "target": 503, "value": 9}, {"source": 549, "target": 513, "value": 8}, {"source": 549, "target": 528, "value": 8}, {"source": 549, "target": 545, "value": 8}, {"source": 551, "target": 251, "value": 4}, {"source": 551, "target": 341, "value": 10}, {"source": 551, "target": 351, "value": 4}, {"source": 551, "target": 401, "value": 4}, {"source": 551, "target": 489, "value": 7}, {"source": 551, "target": 501, "value": 3}, {"source": 551, "target": 503, "value": 9}, {"source": 551, "target": 525, "value": 8}, {"source": 555, "target": 245, "value": 10}, {"source": 555, "target": 251, "value": 8}, {"source": 555, "target": 255, "value": 4}, {"source": 555, "target": 293, "value": 8}, {"source": 555, "target": 305, "value": 4}, {"source": 555, "target": 377, "value": 11}, {"source": 555, "target": 393, "value": 8}, {"source": 555, "target": 405, "value": 4}, {"source": 555, "target": 425, "value": 3}, {"source": 555, "target": 429, "value": 8}, {"source": 555, "target": 455, "value": 4}, {"source": 555, "target": 495, "value": 3}, {"source": 557, "target": 219, "value": 8}, {"source": 557, "target": 233, "value": 3}, {"source": 557, "target": 245, "value": 8}, {"source": 557, "target": 248, "value": 8}, {"source": 557, "target": 255, "value": 8}, {"source": 557, "target": 257, "value": 2}, {"source": 557, "target": 273, "value": 11}, {"source": 557, "target": 305, "value": 8}, {"source": 557, "target": 309, "value": 9}, {"source": 557, "target": 317, "value": 9}, {"source": 557, "target": 323, "value": 10}, {"source": 557, "target": 333, "value": 3}, {"source": 557, "target": 335, "value": 9}, {"source": 557, "target": 345, "value": 8}, {"source": 557, "target": 348, "value": 8}, {"source": 557, "target": 363, "value": 9}, {"source": 557, "target": 369, "value": 8}, {"source": 557, "target": 381, "value": 8}, {"source": 557, "target": 383, "value": 4}, {"source": 557, "target": 398, "value": 8}, {"source": 557, "target": 405, "value": 4}, {"source": 557, "target": 407, "value": 4}, {"source": 557, "target": 413, "value": 3}, {"source": 557, "target": 423, "value": 9}, {"source": 557, "target": 467, "value": 9}, {"source": 557, "target": 473, "value": 9}, {"source": 557, "target": 483, "value": 2}, {"source": 557, "target": 498, "value": 8}, {"source": 557, "target": 503, "value": 7}, {"source": 557, "target": 519, "value": 8}, {"source": 557, "target": 533, "value": 2}, {"source": 557, "target": 545, "value": 8}, {"source": 557, "target": 548, "value": 7}, {"source": 561, "target": 249, "value": 8}, {"source": 561, "target": 261, "value": 2}, {"source": 561, "target": 273, "value": 8}, {"source": 561, "target": 285, "value": 8}, {"source": 561, "target": 311, "value": 4}, {"source": 561, "target": 323, "value": 8}, {"source": 561, "target": 363, "value": 10}, {"source": 561, "target": 411, "value": 4}, {"source": 561, "target": 413, "value": 9}, {"source": 561, "target": 423, "value": 8}, {"source": 561, "target": 459, "value": 3}, {"source": 561, "target": 461, "value": 2}, {"source": 561, "target": 473, "value": 8}, {"source": 561, "target": 501, "value": 9}, {"source": 561, "target": 549, "value": 8}, {"source": 563, "target": 225, "value": 8}, {"source": 563, "target": 228, "value": 9}, {"source": 563, "target": 263, "value": 2}, {"source": 563, "target": 275, "value": 8}, {"source": 563, "target": 278, "value": 9}, {"source": 563, "target": 287, "value": 8}, {"source": 563, "target": 309, "value": 9}, {"source": 563, "target": 321, "value": 8}, {"source": 563, "target": 351, "value": 8}, {"source": 563, "target": 363, "value": 2}, {"source": 563, "target": 365, "value": 9}, {"source": 563, "target": 375, "value": 8}, {"source": 563, "target": 378, "value": 9}, {"source": 563, "target": 401, "value": 8}, {"source": 563, "target": 405, "value": 9}, {"source": 563, "target": 413, "value": 1}, {"source": 563, "target": 425, "value": 8}, {"source": 563, "target": 428, "value": 9}, {"source": 563, "target": 437, "value": 8}, {"source": 563, "target": 453, "value": 9}, {"source": 563, "target": 455, "value": 10}, {"source": 563, "target": 459, "value": 9}, {"source": 563, "target": 461, "value": 8}, {"source": 563, "target": 489, "value": 8}, {"source": 563, "target": 497, "value": 4}, {"source": 563, "target": 501, "value": 8}, {"source": 563, "target": 513, "value": 1}, {"source": 563, "target": 525, "value": 8}, {"source": 563, "target": 528, "value": 8}, {"source": 563, "target": 549, "value": 8}, {"source": 573, "target": 219, "value": 9}, {"source": 573, "target": 221, "value": 2}, {"source": 573, "target": 227, "value": 8}, {"source": 573, "target": 233, "value": 8}, {"source": 573, "target": 248, "value": 8}, {"source": 573, "target": 249, "value": 8}, {"source": 573, "target": 251, "value": 4}, {"source": 573, "target": 257, "value": 8}, {"source": 573, "target": 261, "value": 8}, {"source": 573, "target": 273, "value": 1}, {"source": 573, "target": 279, "value": 8}, {"source": 573, "target": 285, "value": 8}, {"source": 573, "target": 293, "value": 10}, {"source": 573, "target": 309, "value": 8}, {"source": 573, "target": 317, "value": 10}, {"source": 573, "target": 321, "value": 2}, {"source": 573, "target": 323, "value": 1}, {"source": 573, "target": 333, "value": 9}, {"source": 573, "target": 335, "value": 8}, {"source": 573, "target": 347, "value": 4}, {"source": 573, "target": 348, "value": 8}, {"source": 573, "target": 351, "value": 9}, {"source": 573, "target": 365, "value": 4}, {"source": 573, "target": 369, "value": 9}, {"source": 573, "target": 371, "value": 9}, {"source": 573, "target": 377, "value": 4}, {"source": 573, "target": 383, "value": 9}, {"source": 573, "target": 398, "value": 8}, {"source": 573, "target": 399, "value": 8}, {"source": 573, "target": 401, "value": 9}, {"source": 573, "target": 413, "value": 9}, {"source": 573, "target": 423, "value": 1}, {"source": 573, "target": 429, "value": 3}, {"source": 573, "target": 435, "value": 8}, {"source": 573, "target": 461, "value": 8}, {"source": 573, "target": 465, "value": 8}, {"source": 573, "target": 471, "value": 9}, {"source": 573, "target": 473, "value": 1}, {"source": 573, "target": 483, "value": 8}, {"source": 573, "target": 485, "value": 8}, {"source": 573, "target": 497, "value": 4}, {"source": 573, "target": 498, "value": 8}, {"source": 573, "target": 501, "value": 9}, {"source": 573, "target": 503, "value": 7}, {"source": 573, "target": 519, "value": 9}, {"source": 573, "target": 521, "value": 4}, {"source": 573, "target": 525, "value": 4}, {"source": 573, "target": 527, "value": 7}, {"source": 573, "target": 531, "value": 7}, {"source": 573, "target": 533, "value": 9}, {"source": 573, "target": 545, "value": 9}, {"source": 573, "target": 548, "value": 8}, {"source": 573, "target": 549, "value": 8}, {"source": 573, "target": 551, "value": 9}, {"source": 573, "target": 557, "value": 8}, {"source": 573, "target": 561, "value": 8}, {"source": 575, "target": 225, "value": 4}, {"source": 575, "target": 228, "value": 8}, {"source": 575, "target": 275, "value": 4}, {"source": 575, "target": 278, "value": 8}, {"source": 575, "target": 321, "value": 8}, {"source": 575, "target": 365, "value": 10}, {"source": 575, "target": 375, "value": 4}, {"source": 575, "target": 377, "value": 10}, {"source": 575, "target": 378, "value": 8}, {"source": 575, "target": 413, "value": 8}, {"source": 575, "target": 425, "value": 4}, {"source": 575, "target": 428, "value": 8}, {"source": 575, "target": 513, "value": 8}, {"source": 575, "target": 525, "value": 3}, {"source": 575, "target": 528, "value": 8}, {"source": 575, "target": 557, "value": 3}, {"source": 575, "target": 563, "value": 8}, {"source": 578, "target": 225, "value": 8}, {"source": 578, "target": 228, "value": 4}, {"source": 578, "target": 249, "value": 8}, {"source": 578, "target": 275, "value": 8}, {"source": 578, "target": 278, "value": 4}, {"source": 578, "target": 303, "value": 10}, {"source": 578, "target": 321, "value": 8}, {"source": 578, "target": 353, "value": 10}, {"source": 578, "target": 365, "value": 10}, {"source": 578, "target": 375, "value": 8}, {"source": 578, "target": 378, "value": 4}, {"source": 578, "target": 399, "value": 8}, {"source": 578, "target": 413, "value": 8}, {"source": 578, "target": 425, "value": 8}, {"source": 578, "target": 428, "value": 4}, {"source": 578, "target": 437, "value": 8}, {"source": 578, "target": 453, "value": 9}, {"source": 578, "target": 503, "value": 8}, {"source": 578, "target": 513, "value": 8}, {"source": 578, "target": 525, "value": 7}, {"source": 578, "target": 528, "value": 3}, {"source": 578, "target": 545, "value": 8}, {"source": 578, "target": 549, "value": 8}, {"source": 578, "target": 563, "value": 9}, {"source": 578, "target": 575, "value": 8}, {"source": 579, "target": 221, "value": 11}, {"source": 579, "target": 233, "value": 12}, {"source": 579, "target": 279, "value": 4}, {"source": 579, "target": 317, "value": 8}, {"source": 579, "target": 333, "value": 11}, {"source": 579, "target": 383, "value": 10}, {"source": 579, "target": 393, "value": 3}, {"source": 579, "target": 429, "value": 4}, {"source": 579, "target": 483, "value": 10}, {"source": 579, "target": 531, "value": 9}, {"source": 579, "target": 533, "value": 2}, {"source": 581, "target": 221, "value": 11}, {"source": 581, "target": 231, "value": 4}, {"source": 581, "target": 243, "value": 8}, {"source": 581, "target": 273, "value": 11}, {"source": 581, "target": 281, "value": 2}, {"source": 581, "target": 291, "value": 8}, {"source": 581, "target": 293, "value": 8}, {"source": 581, "target": 333, "value": 9}, {"source": 581, "target": 341, "value": 8}, {"source": 581, "target": 369, "value": 8}, {"source": 581, "target": 377, "value": 8}, {"source": 581, "target": 381, "value": 1}, {"source": 581, "target": 393, "value": 8}, {"source": 581, "target": 398, "value": 9}, {"source": 581, "target": 429, "value": 8}, {"source": 581, "target": 431, "value": 2}, {"source": 581, "target": 441, "value": 9}, {"source": 581, "target": 443, "value": 8}, {"source": 581, "target": 489, "value": 7}, {"source": 581, "target": 491, "value": 8}, {"source": 581, "target": 519, "value": 8}, {"source": 581, "target": 521, "value": 3}, {"source": 581, "target": 531, "value": 3}, {"source": 581, "target": 533, "value": 4}, {"source": 581, "target": 543, "value": 8}, {"source": 585, "target": 243, "value": 9}, {"source": 585, "target": 273, "value": 8}, {"source": 585, "target": 281, "value": 8}, {"source": 585, "target": 285, "value": 2}, {"source": 585, "target": 293, "value": 2}, {"source": 585, "target": 333, "value": 8}, {"source": 585, "target": 335, "value": 4}, {"source": 585, "target": 347, "value": 4}, {"source": 585, "target": 377, "value": 10}, {"source": 585, "target": 381, "value": 8}, {"source": 585, "target": 393, "value": 9}, {"source": 585, "target": 398, "value": 8}, {"source": 585, "target": 431, "value": 8}, {"source": 585, "target": 435, "value": 4}, {"source": 585, "target": 437, "value": 4}, {"source": 585, "target": 443, "value": 9}, {"source": 585, "target": 473, "value": 8}, {"source": 585, "target": 485, "value": 2}, {"source": 585, "target": 489, "value": 9}, {"source": 585, "target": 497, "value": 4}, {"source": 585, "target": 525, "value": 9}, {"source": 585, "target": 531, "value": 7}, {"source": 585, "target": 533, "value": 7}, {"source": 585, "target": 543, "value": 9}, {"source": 585, "target": 573, "value": 8}, {"source": 585, "target": 581, "value": 8}, {"source": 587, "target": 225, "value": 9}, {"source": 587, "target": 263, "value": 9}, {"source": 587, "target": 285, "value": 9}, {"source": 587, "target": 287, "value": 4}, {"source": 587, "target": 291, "value": 8}, {"source": 587, "target": 335, "value": 9}, {"source": 587, "target": 341, "value": 8}, {"source": 587, "target": 363, "value": 9}, {"source": 587, "target": 381, "value": 10}, {"source": 587, "target": 413, "value": 4}, {"source": 587, "target": 425, "value": 8}, {"source": 587, "target": 429, "value": 4}, {"source": 587, "target": 435, "value": 9}, {"source": 587, "target": 437, "value": 4}, {"source": 587, "target": 441, "value": 8}, {"source": 587, "target": 461, "value": 8}, {"source": 587, "target": 485, "value": 9}, {"source": 587, "target": 489, "value": 9}, {"source": 587, "target": 491, "value": 8}, {"source": 587, "target": 513, "value": 4}, {"source": 587, "target": 525, "value": 7}, {"source": 587, "target": 533, "value": 9}, {"source": 587, "target": 563, "value": 8}, {"source": 587, "target": 581, "value": 8}, {"source": 587, "target": 585, "value": 8}, {"source": 591, "target": 291, "value": 4}, {"source": 591, "target": 293, "value": 10}, {"source": 591, "target": 341, "value": 4}, {"source": 591, "target": 365, "value": 8}, {"source": 591, "target": 381, "value": 10}, {"source": 591, "target": 429, "value": 8}, {"source": 591, "target": 441, "value": 4}, {"source": 591, "target": 491, "value": 4}, {"source": 591, "target": 533, "value": 9}, {"source": 591, "target": 581, "value": 8}, {"source": 591, "target": 587, "value": 8}, {"source": 593, "target": 243, "value": 1}, {"source": 593, "target": 245, "value": 10}, {"source": 593, "target": 251, "value": 8}, {"source": 593, "target": 255, "value": 8}, {"source": 593, "target": 281, "value": 8}, {"source": 593, "target": 293, "value": 1}, {"source": 593, "target": 305, "value": 8}, {"source": 593, "target": 317, "value": 2}, {"source": 593, "target": 333, "value": 10}, {"source": 593, "target": 335, "value": 9}, {"source": 593, "target": 339, "value": 9}, {"source": 593, "target": 377, "value": 8}, {"source": 593, "target": 381, "value": 8}, {"source": 593, "target": 393, "value": 1}, {"source": 593, "target": 398, "value": 9}, {"source": 593, "target": 405, "value": 8}, {"source": 593, "target": 429, "value": 8}, {"source": 593, "target": 431, "value": 9}, {"source": 593, "target": 443, "value": 1}, {"source": 593, "target": 455, "value": 8}, {"source": 593, "target": 467, "value": 4}, {"source": 593, "target": 489, "value": 2}, {"source": 593, "target": 491, "value": 3}, {"source": 593, "target": 533, "value": 8}, {"source": 593, "target": 543, "value": 1}, {"source": 593, "target": 545, "value": 9}, {"source": 593, "target": 555, "value": 8}, {"source": 593, "target": 561, "value": 3}, {"source": 593, "target": 581, "value": 8}, {"source": 593, "target": 585, "value": 8}, {"source": 603, "target": 227, "value": 8}, {"source": 603, "target": 228, "value": 8}, {"source": 603, "target": 249, "value": 9}, {"source": 603, "target": 278, "value": 8}, {"source": 603, "target": 279, "value": 9}, {"source": 603, "target": 293, "value": 10}, {"source": 603, "target": 303, "value": 2}, {"source": 603, "target": 341, "value": 8}, {"source": 603, "target": 353, "value": 1}, {"source": 603, "target": 377, "value": 8}, {"source": 603, "target": 378, "value": 8}, {"source": 603, "target": 399, "value": 9}, {"source": 603, "target": 405, "value": 9}, {"source": 603, "target": 428, "value": 8}, {"source": 603, "target": 437, "value": 8}, {"source": 603, "target": 441, "value": 8}, {"source": 603, "target": 453, "value": 1}, {"source": 603, "target": 503, "value": 2}, {"source": 603, "target": 527, "value": 8}, {"source": 603, "target": 528, "value": 8}, {"source": 603, "target": 545, "value": 9}, {"source": 603, "target": 549, "value": 9}, {"source": 603, "target": 578, "value": 8}, {"source": 605, "target": 215, "value": 8}, {"source": 605, "target": 231, "value": 8}, {"source": 605, "target": 245, "value": 10}, {"source": 605, "target": 251, "value": 8}, {"source": 605, "target": 255, "value": 2}, {"source": 605, "target": 281, "value": 8}, {"source": 605, "target": 293, "value": 8}, {"source": 605, "target": 305, "value": 2}, {"source": 605, "target": 315, "value": 8}, {"source": 605, "target": 317, "value": 8}, {"source": 605, "target": 353, "value": 8}, {"source": 605, "target": 363, "value": 8}, {"source": 605, "target": 365, "value": 8}, {"source": 605, "target": 377, "value": 11}, {"source": 605, "target": 381, "value": 8}, {"source": 605, "target": 393, "value": 8}, {"source": 605, "target": 405, "value": 2}, {"source": 605, "target": 429, "value": 8}, {"source": 605, "target": 431, "value": 8}, {"source": 605, "target": 453, "value": 4}, {"source": 605, "target": 455, "value": 4}, {"source": 605, "target": 461, "value": 8}, {"source": 605, "target": 465, "value": 9}, {"source": 605, "target": 467, "value": 8}, {"source": 605, "target": 515, "value": 8}, {"source": 605, "target": 531, "value": 8}, {"source": 605, "target": 555, "value": 3}, {"source": 605, "target": 557, "value": 8}, {"source": 605, "target": 581, "value": 8}, {"source": 605, "target": 593, "value": 8}, {"source": 609, "target": 221, "value": 8}, {"source": 609, "target": 261, "value": 10}, {"source": 609, "target": 263, "value": 12}, {"source": 609, "target": 309, "value": 2}, {"source": 609, "target": 321, "value": 8}, {"source": 609, "target": 333, "value": 8}, {"source": 609, "target": 351, "value": 10}, {"source": 609, "target": 363, "value": 11}, {"source": 609, "target": 371, "value": 8}, {"source": 609, "target": 405, "value": 8}, {"source": 609, "target": 413, "value": 10}, {"source": 609, "target": 459, "value": 4}, {"source": 609, "target": 461, "value": 4}, {"source": 609, "target": 471, "value": 8}, {"source": 609, "target": 497, "value": 4}, {"source": 609, "target": 513, "value": 10}, {"source": 609, "target": 521, "value": 8}, {"source": 609, "target": 549, "value": 8}, {"source": 609, "target": 563, "value": 9}, {"source": 611, "target": 219, "value": 8}, {"source": 611, "target": 248, "value": 8}, {"source": 611, "target": 249, "value": 9}, {"source": 611, "target": 257, "value": 8}, {"source": 611, "target": 261, "value": 4}, {"source": 611, "target": 273, "value": 9}, {"source": 611, "target": 285, "value": 8}, {"source": 611, "target": 309, "value": 8}, {"source": 611, "target": 311, "value": 4}, {"source": 611, "target": 323, "value": 9}, {"source": 611, "target": 348, "value": 8}, {"source": 611, "target": 363, "value": 10}, {"source": 611, "target": 369, "value": 9}, {"source": 611, "target": 398, "value": 8}, {"source": 611, "target": 411, "value": 4}, {"source": 611, "target": 423, "value": 9}, {"source": 611, "target": 461, "value": 4}, {"source": 611, "target": 473, "value": 8}, {"source": 611, "target": 498, "value": 8}, {"source": 611, "target": 501, "value": 9}, {"source": 611, "target": 519, "value": 8}, {"source": 611, "target": 548, "value": 8}, {"source": 611, "target": 549, "value": 7}, {"source": 611, "target": 557, "value": 8}, {"source": 611, "target": 561, "value": 3}, {"source": 611, "target": 573, "value": 8}, {"source": 615, "target": 215, "value": 4}, {"source": 615, "target": 219, "value": 8}, {"source": 615, "target": 248, "value": 9}, {"source": 615, "target": 257, "value": 9}, {"source": 615, "target": 263, "value": 8}, {"source": 615, "target": 273, "value": 11}, {"source": 615, "target": 309, "value": 10}, {"source": 615, "target": 315, "value": 4}, {"source": 615, "target": 323, "value": 11}, {"source": 615, "target": 348, "value": 9}, {"source": 615, "target": 353, "value": 8}, {"source": 615, "target": 363, "value": 9}, {"source": 615, "target": 365, "value": 4}, {"source": 615, "target": 369, "value": 8}, {"source": 615, "target": 398, "value": 9}, {"source": 615, "target": 405, "value": 10}, {"source": 615, "target": 413, "value": 9}, {"source": 615, "target": 423, "value": 10}, {"source": 615, "target": 453, "value": 4}, {"source": 615, "target": 461, "value": 8}, {"source": 615, "target": 465, "value": 4}, {"source": 615, "target": 471, "value": 3}, {"source": 615, "target": 473, "value": 10}, {"source": 615, "target": 498, "value": 8}, {"source": 615, "target": 513, "value": 9}, {"source": 615, "target": 515, "value": 4}, {"source": 615, "target": 519, "value": 8}, {"source": 615, "target": 548, "value": 8}, {"source": 615, "target": 557, "value": 8}, {"source": 615, "target": 563, "value": 8}, {"source": 615, "target": 573, "value": 9}, {"source": 615, "target": 605, "value": 8}, {"source": 615, "target": 611, "value": 2}, {"source": 617, "target": 221, "value": 10}, {"source": 617, "target": 233, "value": 11}, {"source": 617, "target": 243, "value": 9}, {"source": 617, "target": 255, "value": 10}, {"source": 617, "target": 279, "value": 8}, {"source": 617, "target": 293, "value": 4}, {"source": 617, "target": 305, "value": 8}, {"source": 617, "target": 317, "value": 2}, {"source": 617, "target": 333, "value": 10}, {"source": 617, "target": 363, "value": 8}, {"source": 617, "target": 383, "value": 10}, {"source": 617, "target": 393, "value": 4}, {"source": 617, "target": 405, "value": 8}, {"source": 617, "target": 429, "value": 8}, {"source": 617, "target": 443, "value": 9}, {"source": 617, "target": 459, "value": 3}, {"source": 617, "target": 467, "value": 4}, {"source": 617, "target": 483, "value": 9}, {"source": 617, "target": 489, "value": 10}, {"source": 617, "target": 533, "value": 9}, {"source": 617, "target": 543, "value": 8}, {"source": 617, "target": 557, "value": 8}, {"source": 617, "target": 579, "value": 8}, {"source": 617, "target": 593, "value": 4}, {"source": 617, "target": 605, "value": 8}, {"source": 621, "target": 221, "value": 1}, {"source": 621, "target": 231, "value": 8}, {"source": 621, "target": 233, "value": 8}, {"source": 621, "target": 261, "value": 10}, {"source": 621, "target": 273, "value": 3}, {"source": 621, "target": 279, "value": 8}, {"source": 621, "target": 281, "value": 8}, {"source": 621, "target": 293, "value": 12}, {"source": 621, "target": 309, "value": 8}, {"source": 621, "target": 321, "value": 2}, {"source": 621, "target": 323, "value": 9}, {"source": 621, "target": 333, "value": 8}, {"source": 621, "target": 347, "value": 8}, {"source": 621, "target": 369, "value": 8}, {"source": 621, "target": 371, "value": 4}, {"source": 621, "target": 377, "value": 8}, {"source": 621, "target": 381, "value": 8}, {"source": 621, "target": 383, "value": 8}, {"source": 621, "target": 423, "value": 8}, {"source": 621, "target": 431, "value": 8}, {"source": 621, "target": 435, "value": 3}, {"source": 621, "target": 461, "value": 9}, {"source": 621, "target": 471, "value": 4}, {"source": 621, "target": 473, "value": 4}, {"source": 621, "target": 483, "value": 8}, {"source": 621, "target": 497, "value": 8}, {"source": 621, "target": 519, "value": 8}, {"source": 621, "target": 521, "value": 2}, {"source": 621, "target": 531, "value": 8}, {"source": 621, "target": 533, "value": 8}, {"source": 621, "target": 573, "value": 2}, {"source": 621, "target": 575, "value": 3}, {"source": 621, "target": 581, "value": 8}, {"source": 621, "target": 609, "value": 8}, {"source": 623, "target": 219, "value": 9}, {"source": 623, "target": 221, "value": 8}, {"source": 623, "target": 248, "value": 8}, {"source": 623, "target": 257, "value": 8}, {"source": 623, "target": 261, "value": 9}, {"source": 623, "target": 273, "value": 1}, {"source": 623, "target": 309, "value": 8}, {"source": 623, "target": 323, "value": 2}, {"source": 623, "target": 347, "value": 8}, {"source": 623, "target": 348, "value": 8}, {"source": 623, "target": 369, "value": 9}, {"source": 623, "target": 398, "value": 8}, {"source": 623, "target": 413, "value": 10}, {"source": 623, "target": 423, "value": 2}, {"source": 623, "target": 461, "value": 8}, {"source": 623, "target": 473, "value": 1}, {"source": 623, "target": 497, "value": 8}, {"source": 623, "target": 498, "value": 8}, {"source": 623, "target": 519, "value": 9}, {"source": 623, "target": 545, "value": 10}, {"source": 623, "target": 548, "value": 8}, {"source": 623, "target": 557, "value": 8}, {"source": 623, "target": 561, "value": 7}, {"source": 623, "target": 573, "value": 1}, {"source": 623, "target": 611, "value": 7}, {"source": 623, "target": 615, "value": 8}, {"source": 623, "target": 621, "value": 7}, {"source": 633, "target": 221, "value": 4}, {"source": 633, "target": 233, "value": 0}, {"source": 633, "target": 245, "value": 8}, {"source": 633, "target": 248, "value": 8}, {"source": 633, "target": 257, "value": 4}, {"source": 633, "target": 279, "value": 4}, {"source": 633, "target": 285, "value": 10}, {"source": 633, "target": 317, "value": 8}, {"source": 633, "target": 321, "value": 4}, {"source": 633, "target": 333, "value": 1}, {"source": 633, "target": 335, "value": 10}, {"source": 633, "target": 345, "value": 8}, {"source": 633, "target": 348, "value": 8}, {"source": 633, "target": 381, "value": 4}, {"source": 633, "target": 383, "value": 1}, {"source": 633, "target": 395, "value": 8}, {"source": 633, "target": 398, "value": 8}, {"source": 633, "target": 405, "value": 11}, {"source": 633, "target": 407, "value": 4}, {"source": 633, "target": 429, "value": 9}, {"source": 633, "target": 483, "value": 1}, {"source": 633, "target": 485, "value": 9}, {"source": 633, "target": 495, "value": 8}, {"source": 633, "target": 498, "value": 8}, {"source": 633, "target": 521, "value": 8}, {"source": 633, "target": 533, "value": 1}, {"source": 633, "target": 545, "value": 2}, {"source": 633, "target": 548, "value": 8}, {"source": 633, "target": 557, "value": 2}, {"source": 633, "target": 573, "value": 8}, {"source": 633, "target": 579, "value": 9}, {"source": 633, "target": 615, "value": 3}, {"source": 633, "target": 617, "value": 8}, {"source": 633, "target": 621, "value": 8}, {"source": 635, "target": 273, "value": 9}, {"source": 635, "target": 285, "value": 4}, {"source": 635, "target": 335, "value": 4}, {"source": 635, "target": 377, "value": 10}, {"source": 635, "target": 435, "value": 4}, {"source": 635, "target": 473, "value": 8}, {"source": 635, "target": 485, "value": 4}, {"source": 635, "target": 525, "value": 9}, {"source": 635, "target": 531, "value": 8}, {"source": 635, "target": 533, "value": 3}, {"source": 635, "target": 573, "value": 8}, {"source": 635, "target": 585, "value": 3}, {"source": 635, "target": 587, "value": 9}, {"source": 635, "target": 603, "value": 3}, {"source": 639, "target": 243, "value": 12}, {"source": 639, "target": 293, "value": 11}, {"source": 639, "target": 335, "value": 8}, {"source": 639, "target": 339, "value": 4}, {"source": 639, "target": 377, "value": 8}, {"source": 639, "target": 393, "value": 11}, {"source": 639, "target": 413, "value": 8}, {"source": 639, "target": 429, "value": 10}, {"source": 639, "target": 443, "value": 10}, {"source": 639, "target": 461, "value": 10}, {"source": 639, "target": 489, "value": 4}, {"source": 639, "target": 543, "value": 10}, {"source": 639, "target": 579, "value": 3}, {"source": 639, "target": 593, "value": 9}, {"source": 641, "target": 291, "value": 4}, {"source": 641, "target": 293, "value": 5}, {"source": 641, "target": 303, "value": 8}, {"source": 641, "target": 341, "value": 2}, {"source": 641, "target": 353, "value": 8}, {"source": 641, "target": 365, "value": 8}, {"source": 641, "target": 381, "value": 10}, {"source": 641, "target": 429, "value": 8}, {"source": 641, "target": 441, "value": 2}, {"source": 641, "target": 453, "value": 8}, {"source": 641, "target": 491, "value": 4}, {"source": 641, "target": 497, "value": 3}, {"source": 641, "target": 503, "value": 8}, {"source": 641, "target": 533, "value": 9}, {"source": 641, "target": 581, "value": 8}, {"source": 641, "target": 587, "value": 7}, {"source": 641, "target": 591, "value": 3}, {"source": 641, "target": 603, "value": 8}, {"source": 645, "target": 233, "value": 4}, {"source": 645, "target": 245, "value": 2}, {"source": 645, "target": 248, "value": 8}, {"source": 645, "target": 251, "value": 8}, {"source": 645, "target": 255, "value": 8}, {"source": 645, "target": 257, "value": 8}, {"source": 645, "target": 285, "value": 10}, {"source": 645, "target": 293, "value": 8}, {"source": 645, "target": 305, "value": 8}, {"source": 645, "target": 333, "value": 8}, {"source": 645, "target": 345, "value": 2}, {"source": 645, "target": 348, "value": 8}, {"source": 645, "target": 393, "value": 8}, {"source": 645, "target": 395, "value": 4}, {"source": 645, "target": 398, "value": 8}, {"source": 645, "target": 405, "value": 8}, {"source": 645, "target": 407, "value": 8}, {"source": 645, "target": 455, "value": 8}, {"source": 645, "target": 485, "value": 9}, {"source": 645, "target": 495, "value": 4}, {"source": 645, "target": 498, "value": 8}, {"source": 645, "target": 503, "value": 8}, {"source": 645, "target": 533, "value": 8}, {"source": 645, "target": 545, "value": 2}, {"source": 645, "target": 548, "value": 8}, {"source": 645, "target": 555, "value": 8}, {"source": 645, "target": 557, "value": 8}, {"source": 645, "target": 593, "value": 7}, {"source": 645, "target": 605, "value": 8}, {"source": 645, "target": 633, "value": 8}, {"source": 647, "target": 221, "value": 9}, {"source": 647, "target": 273, "value": 4}, {"source": 647, "target": 285, "value": 9}, {"source": 647, "target": 293, "value": 8}, {"source": 647, "target": 323, "value": 9}, {"source": 647, "target": 347, "value": 4}, {"source": 647, "target": 423, "value": 9}, {"source": 647, "target": 437, "value": 9}, {"source": 647, "target": 473, "value": 4}, {"source": 647, "target": 485, "value": 8}, {"source": 647, "target": 497, "value": 4}, {"source": 647, "target": 573, "value": 4}, {"source": 647, "target": 585, "value": 4}, {"source": 647, "target": 621, "value": 8}, {"source": 647, "target": 623, "value": 8}, {"source": 648, "target": 219, "value": 8}, {"source": 648, "target": 233, "value": 4}, {"source": 648, "target": 245, "value": 8}, {"source": 648, "target": 248, "value": 4}, {"source": 648, "target": 257, "value": 8}, {"source": 648, "target": 273, "value": 11}, {"source": 648, "target": 285, "value": 10}, {"source": 648, "target": 309, "value": 9}, {"source": 648, "target": 323, "value": 10}, {"source": 648, "target": 333, "value": 9}, {"source": 648, "target": 345, "value": 8}, {"source": 648, "target": 348, "value": 4}, {"source": 648, "target": 369, "value": 8}, {"source": 648, "target": 395, "value": 8}, {"source": 648, "target": 398, "value": 4}, {"source": 648, "target": 423, "value": 10}, {"source": 648, "target": 473, "value": 9}, {"source": 648, "target": 485, "value": 9}, {"source": 648, "target": 495, "value": 8}, {"source": 648, "target": 498, "value": 4}, {"source": 648, "target": 519, "value": 8}, {"source": 648, "target": 533, "value": 8}, {"source": 648, "target": 545, "value": 8}, {"source": 648, "target": 548, "value": 4}, {"source": 648, "target": 557, "value": 7}, {"source": 648, "target": 573, "value": 8}, {"source": 648, "target": 611, "value": 8}, {"source": 648, "target": 615, "value": 8}, {"source": 648, "target": 623, "value": 8}, {"source": 648, "target": 633, "value": 8}, {"source": 648, "target": 645, "value": 8}, {"source": 651, "target": 251, "value": 4}, {"source": 651, "target": 263, "value": 8}, {"source": 651, "target": 341, "value": 10}, {"source": 651, "target": 351, "value": 4}, {"source": 651, "target": 363, "value": 8}, {"source": 651, "target": 401, "value": 2}, {"source": 651, "target": 413, "value": 8}, {"source": 651, "target": 453, "value": 8}, {"source": 651, "target": 455, "value": 9}, {"source": 651, "target": 489, "value": 8}, {"source": 651, "target": 501, "value": 2}, {"source": 651, "target": 503, "value": 9}, {"source": 651, "target": 513, "value": 8}, {"source": 651, "target": 525, "value": 8}, {"source": 651, "target": 551, "value": 4}, {"source": 651, "target": 563, "value": 8}, {"source": 651, "target": 573, "value": 9}, {"source": 653, "target": 215, "value": 8}, {"source": 653, "target": 227, "value": 4}, {"source": 653, "target": 228, "value": 8}, {"source": 653, "target": 249, "value": 8}, {"source": 653, "target": 263, "value": 8}, {"source": 653, "target": 278, "value": 8}, {"source": 653, "target": 279, "value": 3}, {"source": 653, "target": 293, "value": 10}, {"source": 653, "target": 303, "value": 1}, {"source": 653, "target": 315, "value": 8}, {"source": 653, "target": 341, "value": 8}, {"source": 653, "target": 353, "value": 1}, {"source": 653, "target": 363, "value": 8}, {"source": 653, "target": 365, "value": 8}, {"source": 653, "target": 377, "value": 4}, {"source": 653, "target": 378, "value": 8}, {"source": 653, "target": 399, "value": 9}, {"source": 653, "target": 401, "value": 8}, {"source": 653, "target": 405, "value": 4}, {"source": 653, "target": 413, "value": 8}, {"source": 653, "target": 428, "value": 8}, {"source": 653, "target": 429, "value": 8}, {"source": 653, "target": 437, "value": 8}, {"source": 653, "target": 441, "value": 8}, {"source": 653, "target": 453, "value": 0}, {"source": 653, "target": 455, "value": 8}, {"source": 653, "target": 461, "value": 8}, {"source": 653, "target": 465, "value": 8}, {"source": 653, "target": 467, "value": 3}, {"source": 653, "target": 501, "value": 8}, {"source": 653, "target": 503, "value": 1}, {"source": 653, "target": 513, "value": 8}, {"source": 653, "target": 515, "value": 8}, {"source": 653, "target": 527, "value": 4}, {"source": 653, "target": 528, "value": 8}, {"source": 653, "target": 531, "value": 8}, {"source": 653, "target": 545, "value": 9}, {"source": 653, "target": 549, "value": 9}, {"source": 653, "target": 563, "value": 8}, {"source": 653, "target": 578, "value": 8}, {"source": 653, "target": 579, "value": 8}, {"source": 653, "target": 603, "value": 1}, {"source": 653, "target": 605, "value": 8}, {"source": 653, "target": 615, "value": 8}, {"source": 653, "target": 641, "value": 8}, {"source": 653, "target": 651, "value": 7}, {"source": 663, "target": 221, "value": 8}, {"source": 663, "target": 261, "value": 9}, {"source": 663, "target": 263, "value": 2}, {"source": 663, "target": 287, "value": 8}, {"source": 663, "target": 309, "value": 4}, {"source": 663, "target": 321, "value": 8}, {"source": 663, "target": 351, "value": 9}, {"source": 663, "target": 363, "value": 2}, {"source": 663, "target": 371, "value": 8}, {"source": 663, "target": 401, "value": 8}, {"source": 663, "target": 405, "value": 8}, {"source": 663, "target": 413, "value": 1}, {"source": 663, "target": 437, "value": 8}, {"source": 663, "target": 453, "value": 9}, {"source": 663, "target": 455, "value": 10}, {"source": 663, "target": 459, "value": 9}, {"source": 663, "target": 461, "value": 4}, {"source": 663, "target": 471, "value": 8}, {"source": 663, "target": 489, "value": 8}, {"source": 663, "target": 497, "value": 4}, {"source": 663, "target": 501, "value": 8}, {"source": 663, "target": 513, "value": 1}, {"source": 663, "target": 521, "value": 9}, {"source": 663, "target": 549, "value": 8}, {"source": 663, "target": 563, "value": 2}, {"source": 663, "target": 587, "value": 8}, {"source": 663, "target": 609, "value": 4}, {"source": 663, "target": 615, "value": 8}, {"source": 663, "target": 621, "value": 8}, {"source": 663, "target": 651, "value": 8}, {"source": 663, "target": 653, "value": 8}, {"source": 665, "target": 215, "value": 4}, {"source": 665, "target": 227, "value": 8}, {"source": 665, "target": 315, "value": 4}, {"source": 665, "target": 317, "value": 10}, {"source": 665, "target": 353, "value": 8}, {"source": 665, "target": 365, "value": 2}, {"source": 665, "target": 377, "value": 8}, {"source": 665, "target": 405, "value": 10}, {"source": 665, "target": 453, "value": 4}, {"source": 665, "target": 461, "value": 8}, {"source": 665, "target": 465, "value": 2}, {"source": 665, "target": 515, "value": 4}, {"source": 665, "target": 527, "value": 8}, {"source": 665, "target": 573, "value": 7}, {"source": 665, "target": 605, "value": 8}, {"source": 665, "target": 615, "value": 3}, {"source": 665, "target": 653, "value": 8}, {"source": 669, "target": 219, "value": 4}, {"source": 669, "target": 221, "value": 5}, {"source": 669, "target": 231, "value": 8}, {"source": 669, "target": 233, "value": 10}, {"source": 669, "target": 245, "value": 8}, {"source": 669, "target": 248, "value": 9}, {"source": 669, "target": 257, "value": 9}, {"source": 669, "target": 273, "value": 5}, {"source": 669, "target": 279, "value": 8}, {"source": 669, "target": 281, "value": 8}, {"source": 669, "target": 309, "value": 10}, {"source": 669, "target": 317, "value": 8}, {"source": 669, "target": 321, "value": 10}, {"source": 669, "target": 323, "value": 11}, {"source": 669, "target": 333, "value": 10}, {"source": 669, "target": 345, "value": 8}, {"source": 669, "target": 348, "value": 9}, {"source": 669, "target": 369, "value": 2}, {"source": 669, "target": 377, "value": 8}, {"source": 669, "target": 381, "value": 8}, {"source": 669, "target": 383, "value": 9}, {"source": 669, "target": 395, "value": 8}, {"source": 669, "target": 398, "value": 9}, {"source": 669, "target": 423, "value": 10}, {"source": 669, "target": 429, "value": 8}, {"source": 669, "target": 431, "value": 8}, {"source": 669, "target": 473, "value": 10}, {"source": 669, "target": 483, "value": 9}, {"source": 669, "target": 495, "value": 8}, {"source": 669, "target": 498, "value": 8}, {"source": 669, "target": 519, "value": 2}, {"source": 669, "target": 531, "value": 8}, {"source": 669, "target": 533, "value": 8}, {"source": 669, "target": 545, "value": 8}, {"source": 669, "target": 548, "value": 8}, {"source": 669, "target": 557, "value": 8}, {"source": 669, "target": 573, "value": 9}, {"source": 669, "target": 579, "value": 8}, {"source": 669, "target": 581, "value": 8}, {"source": 669, "target": 611, "value": 9}, {"source": 669, "target": 615, "value": 7}, {"source": 669, "target": 617, "value": 7}, {"source": 669, "target": 621, "value": 8}, {"source": 669, "target": 623, "value": 9}, {"source": 669, "target": 633, "value": 8}, {"source": 669, "target": 645, "value": 8}, {"source": 669, "target": 648, "value": 7}, {"source": 671, "target": 219, "value": 8}, {"source": 671, "target": 221, "value": 4}, {"source": 671, "target": 225, "value": 8}, {"source": 671, "target": 228, "value": 8}, {"source": 671, "target": 261, "value": 10}, {"source": 671, "target": 275, "value": 8}, {"source": 671, "target": 278, "value": 8}, {"source": 671, "target": 293, "value": 12}, {"source": 671, "target": 309, "value": 8}, {"source": 671, "target": 321, "value": 2}, {"source": 671, "target": 365, "value": 10}, {"source": 671, "target": 369, "value": 8}, {"source": 671, "target": 371, "value": 4}, {"source": 671, "target": 375, "value": 8}, {"source": 671, "target": 378, "value": 8}, {"source": 671, "target": 413, "value": 8}, {"source": 671, "target": 425, "value": 8}, {"source": 671, "target": 428, "value": 8}, {"source": 671, "target": 461, "value": 9}, {"source": 671, "target": 471, "value": 4}, {"source": 671, "target": 513, "value": 8}, {"source": 671, "target": 519, "value": 8}, {"source": 671, "target": 521, "value": 4}, {"source": 671, "target": 525, "value": 8}, {"source": 671, "target": 528, "value": 8}, {"source": 671, "target": 563, "value": 9}, {"source": 671, "target": 573, "value": 9}, {"source": 671, "target": 575, "value": 7}, {"source": 671, "target": 578, "value": 8}, {"source": 671, "target": 609, "value": 7}, {"source": 671, "target": 621, "value": 3}, {"source": 671, "target": 663, "value": 8}, {"source": 671, "target": 669, "value": 7}, {"source": 675, "target": 225, "value": 4}, {"source": 675, "target": 228, "value": 8}, {"source": 675, "target": 275, "value": 4}, {"source": 675, "target": 278, "value": 8}, {"source": 675, "target": 321, "value": 8}, {"source": 675, "target": 365, "value": 10}, {"source": 675, "target": 375, "value": 4}, {"source": 675, "target": 377, "value": 10}, {"source": 675, "target": 378, "value": 8}, {"source": 675, "target": 413, "value": 8}, {"source": 675, "target": 425, "value": 4}, {"source": 675, "target": 428, "value": 8}, {"source": 675, "target": 513, "value": 8}, {"source": 675, "target": 525, "value": 4}, {"source": 675, "target": 528, "value": 8}, {"source": 675, "target": 563, "value": 8}, {"source": 675, "target": 575, "value": 3}, {"source": 675, "target": 578, "value": 8}, {"source": 675, "target": 587, "value": 3}, {"source": 675, "target": 671, "value": 7}, {"source": 677, "target": 225, "value": 8}, {"source": 677, "target": 227, "value": 4}, {"source": 677, "target": 243, "value": 11}, {"source": 677, "target": 279, "value": 10}, {"source": 677, "target": 287, "value": 8}, {"source": 677, "target": 293, "value": 11}, {"source": 677, "target": 303, "value": 9}, {"source": 677, "target": 317, "value": 10}, {"source": 677, "target": 335, "value": 8}, {"source": 677, "target": 339, "value": 8}, {"source": 677, "target": 353, "value": 4}, {"source": 677, "target": 365, "value": 4}, {"source": 677, "target": 377, "value": 2}, {"source": 677, "target": 393, "value": 10}, {"source": 677, "target": 425, "value": 8}, {"source": 677, "target": 429, "value": 4}, {"source": 677, "target": 437, "value": 8}, {"source": 677, "target": 443, "value": 10}, {"source": 677, "target": 453, "value": 4}, {"source": 677, "target": 465, "value": 8}, {"source": 677, "target": 489, "value": 8}, {"source": 677, "target": 503, "value": 9}, {"source": 677, "target": 525, "value": 8}, {"source": 677, "target": 527, "value": 4}, {"source": 677, "target": 543, "value": 9}, {"source": 677, "target": 573, "value": 8}, {"source": 677, "target": 575, "value": 3}, {"source": 677, "target": 587, "value": 8}, {"source": 677, "target": 593, "value": 9}, {"source": 677, "target": 603, "value": 8}, {"source": 677, "target": 639, "value": 8}, {"source": 677, "target": 645, "value": 3}, {"source": 677, "target": 653, "value": 4}, {"source": 677, "target": 665, "value": 8}, {"source": 678, "target": 225, "value": 8}, {"source": 678, "target": 228, "value": 4}, {"source": 678, "target": 249, "value": 8}, {"source": 678, "target": 261, "value": 8}, {"source": 678, "target": 273, "value": 8}, {"source": 678, "target": 275, "value": 8}, {"source": 678, "target": 278, "value": 4}, {"source": 678, "target": 303, "value": 10}, {"source": 678, "target": 321, "value": 8}, {"source": 678, "target": 323, "value": 8}, {"source": 678, "target": 353, "value": 10}, {"source": 678, "target": 365, "value": 10}, {"source": 678, "target": 375, "value": 8}, {"source": 678, "target": 378, "value": 4}, {"source": 678, "target": 399, "value": 8}, {"source": 678, "target": 413, "value": 4}, {"source": 678, "target": 423, "value": 8}, {"source": 678, "target": 425, "value": 8}, {"source": 678, "target": 428, "value": 4}, {"source": 678, "target": 437, "value": 8}, {"source": 678, "target": 453, "value": 9}, {"source": 678, "target": 461, "value": 8}, {"source": 678, "target": 473, "value": 8}, {"source": 678, "target": 503, "value": 9}, {"source": 678, "target": 513, "value": 8}, {"source": 678, "target": 525, "value": 8}, {"source": 678, "target": 528, "value": 4}, {"source": 678, "target": 545, "value": 8}, {"source": 678, "target": 549, "value": 8}, {"source": 678, "target": 561, "value": 8}, {"source": 678, "target": 563, "value": 9}, {"source": 678, "target": 573, "value": 8}, {"source": 678, "target": 575, "value": 8}, {"source": 678, "target": 578, "value": 4}, {"source": 678, "target": 603, "value": 8}, {"source": 678, "target": 623, "value": 8}, {"source": 678, "target": 653, "value": 8}, {"source": 678, "target": 671, "value": 7}, {"source": 678, "target": 675, "value": 8}, {"source": 681, "target": 221, "value": 11}, {"source": 681, "target": 231, "value": 4}, {"source": 681, "target": 243, "value": 8}, {"source": 681, "target": 273, "value": 11}, {"source": 681, "target": 281, "value": 2}, {"source": 681, "target": 293, "value": 8}, {"source": 681, "target": 333, "value": 10}, {"source": 681, "target": 369, "value": 8}, {"source": 681, "target": 377, "value": 8}, {"source": 681, "target": 381, "value": 2}, {"source": 681, "target": 393, "value": 8}, {"source": 681, "target": 398, "value": 9}, {"source": 681, "target": 431, "value": 2}, {"source": 681, "target": 443, "value": 8}, {"source": 681, "target": 489, "value": 8}, {"source": 681, "target": 519, "value": 9}, {"source": 681, "target": 531, "value": 4}, {"source": 681, "target": 533, "value": 9}, {"source": 681, "target": 543, "value": 8}, {"source": 681, "target": 551, "value": 3}, {"source": 681, "target": 581, "value": 2}, {"source": 681, "target": 585, "value": 9}, {"source": 681, "target": 593, "value": 8}, {"source": 681, "target": 605, "value": 8}, {"source": 681, "target": 621, "value": 2}, {"source": 681, "target": 669, "value": 8}, {"source": 683, "target": 221, "value": 4}, {"source": 683, "target": 233, "value": 1}, {"source": 683, "target": 257, "value": 8}, {"source": 683, "target": 279, "value": 4}, {"source": 683, "target": 317, "value": 8}, {"source": 683, "target": 321, "value": 4}, {"source": 683, "target": 333, "value": 1}, {"source": 683, "target": 335, "value": 10}, {"source": 683, "target": 381, "value": 8}, {"source": 683, "target": 383, "value": 2}, {"source": 683, "target": 405, "value": 11}, {"source": 683, "target": 407, "value": 8}, {"source": 683, "target": 429, "value": 9}, {"source": 683, "target": 483, "value": 2}, {"source": 683, "target": 521, "value": 8}, {"source": 683, "target": 533, "value": 1}, {"source": 683, "target": 557, "value": 4}, {"source": 683, "target": 573, "value": 9}, {"source": 683, "target": 579, "value": 9}, {"source": 683, "target": 617, "value": 8}, {"source": 683, "target": 621, "value": 7}, {"source": 683, "target": 633, "value": 1}, {"source": 683, "target": 669, "value": 8}, {"source": 693, "target": 243, "value": 1}, {"source": 693, "target": 245, "value": 10}, {"source": 693, "target": 251, "value": 8}, {"source": 693, "target": 255, "value": 8}, {"source": 693, "target": 281, "value": 9}, {"source": 693, "target": 293, "value": 0}, {"source": 693, "target": 303, "value": 8}, {"source": 693, "target": 305, "value": 8}, {"source": 693, "target": 317, "value": 2}, {"source": 693, "target": 333, "value": 10}, {"source": 693, "target": 335, "value": 8}, {"source": 693, "target": 339, "value": 8}, {"source": 693, "target": 341, "value": 8}, {"source": 693, "target": 353, "value": 8}, {"source": 693, "target": 377, "value": 8}, {"source": 693, "target": 381, "value": 8}, {"source": 693, "target": 393, "value": 1}, {"source": 693, "target": 398, "value": 9}, {"source": 693, "target": 405, "value": 8}, {"source": 693, "target": 429, "value": 8}, {"source": 693, "target": 431, "value": 9}, {"source": 693, "target": 441, "value": 8}, {"source": 693, "target": 443, "value": 1}, {"source": 693, "target": 453, "value": 8}, {"source": 693, "target": 455, "value": 8}, {"source": 693, "target": 467, "value": 4}, {"source": 693, "target": 489, "value": 2}, {"source": 693, "target": 503, "value": 8}, {"source": 693, "target": 521, "value": 3}, {"source": 693, "target": 533, "value": 9}, {"source": 693, "target": 543, "value": 1}, {"source": 693, "target": 545, "value": 9}, {"source": 693, "target": 555, "value": 8}, {"source": 693, "target": 581, "value": 8}, {"source": 693, "target": 585, "value": 9}, {"source": 693, "target": 591, "value": 3}, {"source": 693, "target": 593, "value": 1}, {"source": 693, "target": 603, "value": 8}, {"source": 693, "target": 605, "value": 8}, {"source": 693, "target": 617, "value": 4}, {"source": 693, "target": 639, "value": 8}, {"source": 693, "target": 641, "value": 7}, {"source": 693, "target": 645, "value": 8}, {"source": 693, "target": 653, "value": 8}, {"source": 693, "target": 677, "value": 8}, {"source": 693, "target": 681, "value": 8}, {"source": 695, "target": 233, "value": 4}, {"source": 695, "target": 245, "value": 2}, {"source": 695, "target": 248, "value": 8}, {"source": 695, "target": 257, "value": 8}, {"source": 695, "target": 285, "value": 10}, {"source": 695, "target": 333, "value": 8}, {"source": 695, "target": 345, "value": 2}, {"source": 695, "target": 348, "value": 8}, {"source": 695, "target": 395, "value": 4}, {"source": 695, "target": 398, "value": 8}, {"source": 695, "target": 407, "value": 8}, {"source": 695, "target": 485, "value": 9}, {"source": 695, "target": 495, "value": 4}, {"source": 695, "target": 498, "value": 8}, {"source": 695, "target": 503, "value": 8}, {"source": 695, "target": 533, "value": 8}, {"source": 695, "target": 545, "value": 2}, {"source": 695, "target": 548, "value": 8}, {"source": 695, "target": 557, "value": 8}, {"source": 695, "target": 579, "value": 3}, {"source": 695, "target": 633, "value": 7}, {"source": 695, "target": 645, "value": 2}, {"source": 695, "target": 648, "value": 8}, {"source": 695, "target": 669, "value": 8}, {"source": 698, "target": 219, "value": 8}, {"source": 698, "target": 233, "value": 4}, {"source": 698, "target": 245, "value": 8}, {"source": 698, "target": 248, "value": 4}, {"source": 698, "target": 257, "value": 8}, {"source": 698, "target": 273, "value": 11}, {"source": 698, "target": 285, "value": 11}, {"source": 698, "target": 309, "value": 10}, {"source": 698, "target": 323, "value": 10}, {"source": 698, "target": 333, "value": 9}, {"source": 698, "target": 345, "value": 8}, {"source": 698, "target": 348, "value": 4}, {"source": 698, "target": 369, "value": 8}, {"source": 698, "target": 395, "value": 8}, {"source": 698, "target": 398, "value": 4}, {"source": 698, "target": 423, "value": 10}, {"source": 698, "target": 473, "value": 9}, {"source": 698, "target": 485, "value": 10}, {"source": 698, "target": 495, "value": 8}, {"source": 698, "target": 498, "value": 4}, {"source": 698, "target": 519, "value": 8}, {"source": 698, "target": 533, "value": 8}, {"source": 698, "target": 545, "value": 8}, {"source": 698, "target": 548, "value": 4}, {"source": 698, "target": 557, "value": 8}, {"source": 698, "target": 573, "value": 9}, {"source": 698, "target": 611, "value": 8}, {"source": 698, "target": 615, "value": 8}, {"source": 698, "target": 623, "value": 8}, {"source": 698, "target": 633, "value": 8}, {"source": 698, "target": 645, "value": 7}, {"source": 698, "target": 648, "value": 3}, {"source": 698, "target": 669, "value": 8}, {"source": 698, "target": 695, "value": 8}, {"source": 699, "target": 221, "value": 8}, {"source": 699, "target": 228, "value": 9}, {"source": 699, "target": 249, "value": 4}, {"source": 699, "target": 251, "value": 10}, {"source": 699, "target": 273, "value": 4}, {"source": 699, "target": 278, "value": 9}, {"source": 699, "target": 293, "value": 10}, {"source": 699, "target": 303, "value": 4}, {"source": 699, "target": 321, "value": 11}, {"source": 699, "target": 323, "value": 8}, {"source": 699, "target": 341, "value": 8}, {"source": 699, "target": 347, "value": 8}, {"source": 699, "target": 353, "value": 4}, {"source": 699, "target": 378, "value": 9}, {"source": 699, "target": 399, "value": 4}, {"source": 699, "target": 423, "value": 8}, {"source": 699, "target": 428, "value": 9}, {"source": 699, "target": 437, "value": 8}, {"source": 699, "target": 441, "value": 8}, {"source": 699, "target": 453, "value": 4}, {"source": 699, "target": 473, "value": 4}, {"source": 699, "target": 497, "value": 8}, {"source": 699, "target": 503, "value": 4}, {"source": 699, "target": 528, "value": 8}, {"source": 699, "target": 545, "value": 8}, {"source": 699, "target": 549, "value": 4}, {"source": 699, "target": 555, "value": 3}, {"source": 699, "target": 573, "value": 2}, {"source": 699, "target": 578, "value": 8}, {"source": 699, "target": 603, "value": 4}, {"source": 699, "target": 621, "value": 8}, {"source": 699, "target": 623, "value": 8}, {"source": 699, "target": 641, "value": 8}, {"source": 699, "target": 647, "value": 8}, {"source": 699, "target": 653, "value": 4}, {"source": 699, "target": 678, "value": 7}, {"source": 699, "target": 693, "value": 8}, {"source": 699, "target": 695, "value": 3}, {"source": 701, "target": 227, "value": 8}, {"source": 701, "target": 249, "value": 8}, {"source": 701, "target": 251, "value": 4}, {"source": 701, "target": 261, "value": 8}, {"source": 701, "target": 263, "value": 8}, {"source": 701, "target": 279, "value": 10}, {"source": 701, "target": 303, "value": 9}, {"source": 701, "target": 311, "value": 8}, {"source": 701, "target": 341, "value": 10}, {"source": 701, "target": 351, "value": 4}, {"source": 701, "target": 353, "value": 4}, {"source": 701, "target": 363, "value": 8}, {"source": 701, "target": 377, "value": 8}, {"source": 701, "target": 401, "value": 2}, {"source": 701, "target": 411, "value": 8}, {"source": 701, "target": 413, "value": 8}, {"source": 701, "target": 453, "value": 3}, {"source": 701, "target": 455, "value": 9}, {"source": 701, "target": 461, "value": 8}, {"source": 701, "target": 489, "value": 8}, {"source": 701, "target": 501, "value": 2}, {"source": 701, "target": 503, "value": 4}, {"source": 701, "target": 513, "value": 8}, {"source": 701, "target": 525, "value": 8}, {"source": 701, "target": 527, "value": 8}, {"source": 701, "target": 543, "value": 3}, {"source": 701, "target": 549, "value": 8}, {"source": 701, "target": 551, "value": 4}, {"source": 701, "target": 561, "value": 8}, {"source": 701, "target": 563, "value": 8}, {"source": 701, "target": 573, "value": 10}, {"source": 701, "target": 603, "value": 8}, {"source": 701, "target": 611, "value": 8}, {"source": 701, "target": 651, "value": 2}, {"source": 701, "target": 653, "value": 2}, {"source": 701, "target": 663, "value": 8}, {"source": 701, "target": 677, "value": 8}, {"source": 701, "target": 683, "value": 3}, {"source": 705, "target": 245, "value": 11}, {"source": 705, "target": 251, "value": 8}, {"source": 705, "target": 255, "value": 2}, {"source": 705, "target": 293, "value": 9}, {"source": 705, "target": 305, "value": 2}, {"source": 705, "target": 317, "value": 8}, {"source": 705, "target": 363, "value": 8}, {"source": 705, "target": 377, "value": 11}, {"source": 705, "target": 393, "value": 8}, {"source": 705, "target": 405, "value": 2}, {"source": 705, "target": 429, "value": 8}, {"source": 705, "target": 455, "value": 4}, {"source": 705, "target": 467, "value": 8}, {"source": 705, "target": 555, "value": 4}, {"source": 705, "target": 557, "value": 9}, {"source": 705, "target": 593, "value": 8}, {"source": 705, "target": 605, "value": 2}, {"source": 705, "target": 617, "value": 8}, {"source": 705, "target": 645, "value": 8}, {"source": 705, "target": 693, "value": 8}, {"source": 707, "target": 233, "value": 4}, {"source": 707, "target": 245, "value": 9}, {"source": 707, "target": 257, "value": 4}, {"source": 707, "target": 333, "value": 4}, {"source": 707, "target": 345, "value": 8}, {"source": 707, "target": 381, "value": 8}, {"source": 707, "target": 383, "value": 9}, {"source": 707, "target": 407, "value": 4}, {"source": 707, "target": 483, "value": 9}, {"source": 707, "target": 503, "value": 8}, {"source": 707, "target": 533, "value": 4}, {"source": 707, "target": 545, "value": 8}, {"source": 707, "target": 557, "value": 4}, {"source": 707, "target": 633, "value": 4}, {"source": 707, "target": 645, "value": 7}, {"source": 707, "target": 683, "value": 8}, {"source": 707, "target": 695, "value": 8}, {"source": 711, "target": 249, "value": 9}, {"source": 711, "target": 261, "value": 4}, {"source": 711, "target": 285, "value": 8}, {"source": 711, "target": 311, "value": 4}, {"source": 711, "target": 363, "value": 10}, {"source": 711, "target": 411, "value": 4}, {"source": 711, "target": 461, "value": 4}, {"source": 711, "target": 501, "value": 9}, {"source": 711, "target": 549, "value": 8}, {"source": 711, "target": 561, "value": 4}, {"source": 711, "target": 611, "value": 4}, {"source": 711, "target": 701, "value": 8}, {"source": 713, "target": 225, "value": 8}, {"source": 713, "target": 228, "value": 8}, {"source": 713, "target": 255, "value": 10}, {"source": 713, "target": 261, "value": 8}, {"source": 713, "target": 263, "value": 1}, {"source": 713, "target": 275, "value": 8}, {"source": 713, "target": 278, "value": 8}, {"source": 713, "target": 285, "value": 8}, {"source": 713, "target": 287, "value": 4}, {"source": 713, "target": 291, "value": 9}, {"source": 713, "target": 293, "value": 8}, {"source": 713, "target": 305, "value": 8}, {"source": 713, "target": 309, "value": 8}, {"source": 713, "target": 311, "value": 8}, {"source": 713, "target": 317, "value": 8}, {"source": 713, "target": 321, "value": 8}, {"source": 713, "target": 341, "value": 9}, {"source": 713, "target": 351, "value": 9}, {"source": 713, "target": 363, "value": 1}, {"source": 713, "target": 365, "value": 4}, {"source": 713, "target": 375, "value": 8}, {"source": 713, "target": 378, "value": 8}, {"source": 713, "target": 401, "value": 8}, {"source": 713, "target": 405, "value": 4}, {"source": 713, "target": 411, "value": 8}, {"source": 713, "target": 413, "value": 1}, {"source": 713, "target": 425, "value": 8}, {"source": 713, "target": 428, "value": 8}, {"source": 713, "target": 437, "value": 4}, {"source": 713, "target": 441, "value": 9}, {"source": 713, "target": 453, "value": 9}, {"source": 713, "target": 455, "value": 10}, {"source": 713, "target": 459, "value": 8}, {"source": 713, "target": 461, "value": 2}, {"source": 713, "target": 467, "value": 8}, {"source": 713, "target": 489, "value": 4}, {"source": 713, "target": 491, "value": 9}, {"source": 713, "target": 497, "value": 4}, {"source": 713, "target": 501, "value": 8}, {"source": 713, "target": 513, "value": 0}, {"source": 713, "target": 525, "value": 8}, {"source": 713, "target": 528, "value": 8}, {"source": 713, "target": 549, "value": 8}, {"source": 713, "target": 557, "value": 9}, {"source": 713, "target": 561, "value": 8}, {"source": 713, "target": 563, "value": 1}, {"source": 713, "target": 575, "value": 8}, {"source": 713, "target": 578, "value": 8}, {"source": 713, "target": 587, "value": 4}, {"source": 713, "target": 591, "value": 9}, {"source": 713, "target": 605, "value": 8}, {"source": 713, "target": 609, "value": 9}, {"source": 713, "target": 611, "value": 8}, {"source": 713, "target": 615, "value": 9}, {"source": 713, "target": 617, "value": 8}, {"source": 713, "target": 641, "value": 9}, {"source": 713, "target": 651, "value": 8}, {"source": 713, "target": 653, "value": 2}, {"source": 713, "target": 663, "value": 1}, {"source": 713, "target": 671, "value": 7}, {"source": 713, "target": 675, "value": 8}, {"source": 713, "target": 678, "value": 8}, {"source": 713, "target": 701, "value": 8}, {"source": 713, "target": 705, "value": 7}, {"source": 713, "target": 711, "value": 7}, {"source": 723, "target": 219, "value": 8}, {"source": 723, "target": 221, "value": 8}, {"source": 723, "target": 248, "value": 8}, {"source": 723, "target": 257, "value": 8}, {"source": 723, "target": 261, "value": 9}, {"source": 723, "target": 273, "value": 1}, {"source": 723, "target": 309, "value": 9}, {"source": 723, "target": 323, "value": 2}, {"source": 723, "target": 347, "value": 8}, {"source": 723, "target": 348, "value": 8}, {"source": 723, "target": 369, "value": 8}, {"source": 723, "target": 398, "value": 8}, {"source": 723, "target": 413, "value": 10}, {"source": 723, "target": 423, "value": 2}, {"source": 723, "target": 461, "value": 8}, {"source": 723, "target": 473, "value": 1}, {"source": 723, "target": 497, "value": 8}, {"source": 723, "target": 498, "value": 8}, {"source": 723, "target": 519, "value": 8}, {"source": 723, "target": 545, "value": 10}, {"source": 723, "target": 548, "value": 8}, {"source": 723, "target": 557, "value": 8}, {"source": 723, "target": 561, "value": 8}, {"source": 723, "target": 573, "value": 1}, {"source": 723, "target": 611, "value": 8}, {"source": 723, "target": 615, "value": 8}, {"source": 723, "target": 621, "value": 8}, {"source": 723, "target": 623, "value": 2}, {"source": 723, "target": 647, "value": 8}, {"source": 723, "target": 648, "value": 8}, {"source": 723, "target": 669, "value": 8}, {"source": 723, "target": 678, "value": 8}, {"source": 723, "target": 698, "value": 8}, {"source": 723, "target": 699, "value": 8}, {"source": 725, "target": 225, "value": 2}, {"source": 725, "target": 228, "value": 8}, {"source": 725, "target": 273, "value": 8}, {"source": 725, "target": 275, "value": 4}, {"source": 725, "target": 278, "value": 8}, {"source": 725, "target": 285, "value": 8}, {"source": 725, "target": 287, "value": 8}, {"source": 725, "target": 321, "value": 8}, {"source": 725, "target": 335, "value": 8}, {"source": 725, "target": 365, "value": 10}, {"source": 725, "target": 375, "value": 4}, {"source": 725, "target": 377, "value": 4}, {"source": 725, "target": 378, "value": 8}, {"source": 725, "target": 413, "value": 8}, {"source": 725, "target": 425, "value": 2}, {"source": 725, "target": 428, "value": 8}, {"source": 725, "target": 429, "value": 10}, {"source": 725, "target": 435, "value": 8}, {"source": 725, "target": 437, "value": 8}, {"source": 725, "target": 473, "value": 8}, {"source": 725, "target": 485, "value": 8}, {"source": 725, "target": 513, "value": 8}, {"source": 725, "target": 525, "value": 2}, {"source": 725, "target": 528, "value": 8}, {"source": 725, "target": 531, "value": 8}, {"source": 725, "target": 563, "value": 9}, {"source": 725, "target": 573, "value": 8}, {"source": 725, "target": 575, "value": 4}, {"source": 725, "target": 578, "value": 8}, {"source": 725, "target": 585, "value": 8}, {"source": 725, "target": 587, "value": 8}, {"source": 725, "target": 635, "value": 8}, {"source": 725, "target": 671, "value": 7}, {"source": 725, "target": 675, "value": 3}, {"source": 725, "target": 677, "value": 8}, {"source": 725, "target": 678, "value": 7}, {"source": 725, "target": 713, "value": 8}, {"source": 728, "target": 225, "value": 8}, {"source": 728, "target": 228, "value": 4}, {"source": 728, "target": 249, "value": 8}, {"source": 728, "target": 275, "value": 8}, {"source": 728, "target": 278, "value": 4}, {"source": 728, "target": 303, "value": 11}, {"source": 728, "target": 321, "value": 8}, {"source": 728, "target": 353, "value": 10}, {"source": 728, "target": 365, "value": 10}, {"source": 728, "target": 375, "value": 8}, {"source": 728, "target": 378, "value": 4}, {"source": 728, "target": 399, "value": 8}, {"source": 728, "target": 413, "value": 8}, {"source": 728, "target": 425, "value": 8}, {"source": 728, "target": 428, "value": 4}, {"source": 728, "target": 437, "value": 8}, {"source": 728, "target": 453, "value": 10}, {"source": 728, "target": 503, "value": 9}, {"source": 728, "target": 513, "value": 8}, {"source": 728, "target": 525, "value": 8}, {"source": 728, "target": 528, "value": 4}, {"source": 728, "target": 545, "value": 8}, {"source": 728, "target": 549, "value": 8}, {"source": 728, "target": 563, "value": 9}, {"source": 728, "target": 575, "value": 8}, {"source": 728, "target": 578, "value": 4}, {"source": 728, "target": 603, "value": 9}, {"source": 728, "target": 653, "value": 8}, {"source": 728, "target": 671, "value": 8}, {"source": 728, "target": 675, "value": 7}, {"source": 728, "target": 678, "value": 3}, {"source": 728, "target": 699, "value": 8}, {"source": 728, "target": 713, "value": 8}, {"source": 728, "target": 725, "value": 8}, {"source": 729, "target": 221, "value": 11}, {"source": 729, "target": 233, "value": 12}, {"source": 729, "target": 279, "value": 4}, {"source": 729, "target": 291, "value": 8}, {"source": 729, "target": 317, "value": 9}, {"source": 729, "target": 333, "value": 11}, {"source": 729, "target": 341, "value": 8}, {"source": 729, "target": 381, "value": 9}, {"source": 729, "target": 383, "value": 11}, {"source": 729, "target": 429, "value": 2}, {"source": 729, "target": 441, "value": 8}, {"source": 729, "target": 483, "value": 10}, {"source": 729, "target": 491, "value": 8}, {"source": 729, "target": 531, "value": 9}, {"source": 729, "target": 533, "value": 4}, {"source": 729, "target": 579, "value": 4}, {"source": 729, "target": 581, "value": 9}, {"source": 729, "target": 587, "value": 8}, {"source": 729, "target": 591, "value": 8}, {"source": 729, "target": 617, "value": 8}, {"source": 729, "target": 633, "value": 9}, {"source": 729, "target": 641, "value": 8}, {"source": 729, "target": 653, "value": 8}, {"source": 729, "target": 669, "value": 8}, {"source": 729, "target": 683, "value": 9}, {"source": 731, "target": 221, "value": 11}, {"source": 731, "target": 231, "value": 4}, {"source": 731, "target": 273, "value": 11}, {"source": 731, "target": 281, "value": 4}, {"source": 731, "target": 369, "value": 8}, {"source": 731, "target": 377, "value": 8}, {"source": 731, "target": 381, "value": 4}, {"source": 731, "target": 431, "value": 4}, {"source": 731, "target": 519, "value": 9}, {"source": 731, "target": 531, "value": 4}, {"source": 731, "target": 581, "value": 4}, {"source": 731, "target": 605, "value": 8}, {"source": 731, "target": 621, "value": 8}, {"source": 731, "target": 669, "value": 7}, {"source": 731, "target": 681, "value": 3}, {"source": 735, "target": 273, "value": 9}, {"source": 735, "target": 285, "value": 4}, {"source": 735, "target": 335, "value": 4}, {"source": 735, "target": 377, "value": 10}, {"source": 735, "target": 435, "value": 4}, {"source": 735, "target": 473, "value": 8}, {"source": 735, "target": 485, "value": 4}, {"source": 735, "target": 525, "value": 9}, {"source": 735, "target": 531, "value": 8}, {"source": 735, "target": 563, "value": 3}, {"source": 735, "target": 573, "value": 8}, {"source": 735, "target": 585, "value": 4}, {"source": 735, "target": 587, "value": 9}, {"source": 735, "target": 633, "value": 3}, {"source": 735, "target": 635, "value": 4}, {"source": 735, "target": 725, "value": 8}, {"source": 737, "target": 225, "value": 9}, {"source": 737, "target": 228, "value": 9}, {"source": 737, "target": 249, "value": 8}, {"source": 737, "target": 263, "value": 9}, {"source": 737, "target": 278, "value": 9}, {"source": 737, "target": 287, "value": 4}, {"source": 737, "target": 303, "value": 11}, {"source": 737, "target": 353, "value": 11}, {"source": 737, "target": 363, "value": 9}, {"source": 737, "target": 378, "value": 8}, {"source": 737, "target": 399, "value": 8}, {"source": 737, "target": 413, "value": 4}, {"source": 737, "target": 425, "value": 8}, {"source": 737, "target": 428, "value": 8}, {"source": 737, "target": 429, "value": 10}, {"source": 737, "target": 437, "value": 2}, {"source": 737, "target": 453, "value": 10}, {"source": 737, "target": 461, "value": 8}, {"source": 737, "target": 489, "value": 9}, {"source": 737, "target": 503, "value": 10}, {"source": 737, "target": 513, "value": 4}, {"source": 737, "target": 525, "value": 8}, {"source": 737, "target": 528, "value": 8}, {"source": 737, "target": 545, "value": 8}, {"source": 737, "target": 549, "value": 8}, {"source": 737, "target": 551, "value": 3}, {"source": 737, "target": 563, "value": 8}, {"source": 737, "target": 578, "value": 8}, {"source": 737, "target": 587, "value": 4}, {"source": 737, "target": 603, "value": 9}, {"source": 737, "target": 621, "value": 3}, {"source": 737, "target": 653, "value": 8}, {"source": 737, "target": 663, "value": 8}, {"source": 737, "target": 677, "value": 8}, {"source": 737, "target": 678, "value": 7}, {"source": 737, "target": 699, "value": 8}, {"source": 737, "target": 713, "value": 4}, {"source": 737, "target": 725, "value": 7}, {"source": 737, "target": 728, "value": 7}, {"source": 741, "target": 219, "value": 8}, {"source": 741, "target": 228, "value": 8}, {"source": 741, "target": 233, "value": 4}, {"source": 741, "target": 245, "value": 8}, {"source": 741, "target": 248, "value": 8}, {"source": 741, "target": 249, "value": 8}, {"source": 741, "target": 251, "value": 8}, {"source": 741, "target": 278, "value": 8}, {"source": 741, "target": 285, "value": 10}, {"source": 741, "target": 291, "value": 4}, {"source": 741, "target": 293, "value": 5}, {"source": 741, "target": 303, "value": 4}, {"source": 741, "target": 321, "value": 9}, {"source": 741, "target": 333, "value": 8}, {"source": 741, "target": 339, "value": 8}, {"source": 741, "target": 341, "value": 2}, {"source": 741, "target": 345, "value": 8}, {"source": 741, "target": 348, "value": 8}, {"source": 741, "target": 351, "value": 8}, {"source": 741, "target": 353, "value": 4}, {"source": 741, "target": 365, "value": 8}, {"source": 741, "target": 369, "value": 8}, {"source": 741, "target": 378, "value": 8}, {"source": 741, "target": 381, "value": 10}, {"source": 741, "target": 395, "value": 8}, {"source": 741, "target": 398, "value": 8}, {"source": 741, "target": 399, "value": 8}, {"source": 741, "target": 401, "value": 8}, {"source": 741, "target": 413, "value": 8}, {"source": 741, "target": 428, "value": 8}, {"source": 741, "target": 429, "value": 8}, {"source": 741, "target": 437, "value": 8}, {"source": 741, "target": 441, "value": 2}, {"source": 741, "target": 453, "value": 4}, {"source": 741, "target": 461, "value": 9}, {"source": 741, "target": 485, "value": 9}, {"source": 741, "target": 489, "value": 4}, {"source": 741, "target": 491, "value": 4}, {"source": 741, "target": 495, "value": 8}, {"source": 741, "target": 498, "value": 8}, {"source": 741, "target": 501, "value": 8}, {"source": 741, "target": 503, "value": 4}, {"source": 741, "target": 519, "value": 9}, {"source": 741, "target": 528, "value": 8}, {"source": 741, "target": 533, "value": 4}, {"source": 741, "target": 545, "value": 4}, {"source": 741, "target": 548, "value": 8}, {"source": 741, "target": 549, "value": 8}, {"source": 741, "target": 551, "value": 8}, {"source": 741, "target": 578, "value": 8}, {"source": 741, "target": 581, "value": 9}, {"source": 741, "target": 587, "value": 8}, {"source": 741, "target": 591, "value": 4}, {"source": 741, "target": 603, "value": 4}, {"source": 741, "target": 633, "value": 8}, {"source": 741, "target": 639, "value": 8}, {"source": 741, "target": 641, "value": 2}, {"source": 741, "target": 645, "value": 7}, {"source": 741, "target": 648, "value": 8}, {"source": 741, "target": 651, "value": 8}, {"source": 741, "target": 653, "value": 4}, {"source": 741, "target": 669, "value": 8}, {"source": 741, "target": 671, "value": 7}, {"source": 741, "target": 678, "value": 7}, {"source": 741, "target": 693, "value": 8}, {"source": 741, "target": 695, "value": 7}, {"source": 741, "target": 698, "value": 7}, {"source": 741, "target": 699, "value": 4}, {"source": 741, "target": 701, "value": 8}, {"source": 741, "target": 713, "value": 8}, {"source": 741, "target": 728, "value": 8}, {"source": 741, "target": 729, "value": 8}, {"source": 741, "target": 737, "value": 2}, {"source": 743, "target": 243, "value": 2}, {"source": 743, "target": 281, "value": 9}, {"source": 743, "target": 293, "value": 1}, {"source": 743, "target": 317, "value": 4}, {"source": 743, "target": 333, "value": 10}, {"source": 743, "target": 335, "value": 8}, {"source": 743, "target": 339, "value": 8}, {"source": 743, "target": 377, "value": 8}, {"source": 743, "target": 381, "value": 8}, {"source": 743, "target": 393, "value": 1}, {"source": 743, "target": 398, "value": 9}, {"source": 743, "target": 429, "value": 8}, {"source": 743, "target": 431, "value": 9}, {"source": 743, "target": 443, "value": 2}, {"source": 743, "target": 467, "value": 8}, {"source": 743, "target": 489, "value": 2}, {"source": 743, "target": 533, "value": 9}, {"source": 743, "target": 543, "value": 2}, {"source": 743, "target": 545, "value": 9}, {"source": 743, "target": 581, "value": 8}, {"source": 743, "target": 585, "value": 9}, {"source": 743, "target": 593, "value": 1}, {"source": 743, "target": 617, "value": 8}, {"source": 743, "target": 639, "value": 8}, {"source": 743, "target": 677, "value": 8}, {"source": 743, "target": 681, "value": 7}, {"source": 743, "target": 693, "value": 1}, {"source": 753, "target": 215, "value": 8}, {"source": 753, "target": 227, "value": 4}, {"source": 753, "target": 228, "value": 8}, {"source": 753, "target": 249, "value": 8}, {"source": 753, "target": 278, "value": 8}, {"source": 753, "target": 279, "value": 4}, {"source": 753, "target": 293, "value": 10}, {"source": 753, "target": 303, "value": 1}, {"source": 753, "target": 315, "value": 8}, {"source": 753, "target": 341, "value": 9}, {"source": 753, "target": 353, "value": 1}, {"source": 753, "target": 365, "value": 8}, {"source": 753, "target": 377, "value": 4}, {"source": 753, "target": 378, "value": 8}, {"source": 753, "target": 399, "value": 8}, {"source": 753, "target": 405, "value": 4}, {"source": 753, "target": 428, "value": 8}, {"source": 753, "target": 437, "value": 8}, {"source": 753, "target": 441, "value": 8}, {"source": 753, "target": 453, "value": 0}, {"source": 753, "target": 461, "value": 8}, {"source": 753, "target": 465, "value": 8}, {"source": 753, "target": 503, "value": 1}, {"source": 753, "target": 515, "value": 8}, {"source": 753, "target": 527, "value": 4}, {"source": 753, "target": 528, "value": 8}, {"source": 753, "target": 545, "value": 8}, {"source": 753, "target": 549, "value": 8}, {"source": 753, "target": 578, "value": 8}, {"source": 753, "target": 603, "value": 1}, {"source": 753, "target": 605, "value": 9}, {"source": 753, "target": 615, "value": 8}, {"source": 753, "target": 641, "value": 8}, {"source": 753, "target": 653, "value": 1}, {"source": 753, "target": 665, "value": 7}, {"source": 753, "target": 677, "value": 4}, {"source": 753, "target": 678, "value": 8}, {"source": 753, "target": 693, "value": 8}, {"source": 753, "target": 699, "value": 4}, {"source": 753, "target": 701, "value": 3}, {"source": 753, "target": 707, "value": 3}, {"source": 753, "target": 728, "value": 8}, {"source": 753, "target": 737, "value": 8}, {"source": 753, "target": 741, "value": 4}, {"source": 755, "target": 245, "value": 11}, {"source": 755, "target": 251, "value": 8}, {"source": 755, "target": 255, "value": 4}, {"source": 755, "target": 263, "value": 12}, {"source": 755, "target": 293, "value": 9}, {"source": 755, "target": 303, "value": 8}, {"source": 755, "target": 305, "value": 4}, {"source": 755, "target": 309, "value": 8}, {"source": 755, "target": 351, "value": 10}, {"source": 755, "target": 353, "value": 8}, {"source": 755, "target": 363, "value": 11}, {"source": 755, "target": 377, "value": 11}, {"source": 755, "target": 393, "value": 8}, {"source": 755, "target": 405, "value": 2}, {"source": 755, "target": 413, "value": 10}, {"source": 755, "target": 429, "value": 8}, {"source": 755, "target": 453, "value": 8}, {"source": 755, "target": 455, "value": 4}, {"source": 755, "target": 459, "value": 8}, {"source": 755, "target": 497, "value": 4}, {"source": 755, "target": 503, "value": 8}, {"source": 755, "target": 513, "value": 10}, {"source": 755, "target": 549, "value": 9}, {"source": 755, "target": 555, "value": 2}, {"source": 755, "target": 563, "value": 9}, {"source": 755, "target": 593, "value": 8}, {"source": 755, "target": 603, "value": 8}, {"source": 755, "target": 605, "value": 4}, {"source": 755, "target": 609, "value": 8}, {"source": 755, "target": 645, "value": 8}, {"source": 755, "target": 653, "value": 8}, {"source": 755, "target": 663, "value": 9}, {"source": 755, "target": 693, "value": 7}, {"source": 755, "target": 695, "value": 3}, {"source": 755, "target": 705, "value": 3}, {"source": 755, "target": 713, "value": 8}, {"source": 755, "target": 753, "value": 7}, {"source": 759, "target": 263, "value": 12}, {"source": 759, "target": 309, "value": 4}, {"source": 759, "target": 333, "value": 8}, {"source": 759, "target": 351, "value": 10}, {"source": 759, "target": 363, "value": 11}, {"source": 759, "target": 405, "value": 8}, {"source": 759, "target": 413, "value": 11}, {"source": 759, "target": 459, "value": 4}, {"source": 759, "target": 461, "value": 9}, {"source": 759, "target": 497, "value": 4}, {"source": 759, "target": 513, "value": 10}, {"source": 759, "target": 549, "value": 9}, {"source": 759, "target": 563, "value": 9}, {"source": 759, "target": 609, "value": 4}, {"source": 759, "target": 663, "value": 9}, {"source": 759, "target": 671, "value": 3}, {"source": 759, "target": 713, "value": 8}, {"source": 759, "target": 741, "value": 3}, {"source": 759, "target": 755, "value": 8}, {"source": 761, "target": 249, "value": 9}, {"source": 761, "target": 261, "value": 2}, {"source": 761, "target": 273, "value": 4}, {"source": 761, "target": 285, "value": 8}, {"source": 761, "target": 311, "value": 4}, {"source": 761, "target": 323, "value": 4}, {"source": 761, "target": 363, "value": 10}, {"source": 761, "target": 411, "value": 4}, {"source": 761, "target": 413, "value": 4}, {"source": 761, "target": 423, "value": 4}, {"source": 761, "target": 461, "value": 2}, {"source": 761, "target": 473, "value": 4}, {"source": 761, "target": 501, "value": 9}, {"source": 761, "target": 549, "value": 8}, {"source": 761, "target": 561, "value": 2}, {"source": 761, "target": 573, "value": 4}, {"source": 761, "target": 611, "value": 4}, {"source": 761, "target": 623, "value": 4}, {"source": 761, "target": 678, "value": 4}, {"source": 761, "target": 701, "value": 8}, {"source": 761, "target": 711, "value": 3}, {"source": 761, "target": 713, "value": 8}, {"source": 761, "target": 723, "value": 4}, {"source": 761, "target": 729, "value": 3}, {"source": 765, "target": 215, "value": 4}, {"source": 765, "target": 225, "value": 8}, {"source": 765, "target": 227, "value": 8}, {"source": 765, "target": 228, "value": 8}, {"source": 765, "target": 275, "value": 8}, {"source": 765, "target": 278, "value": 8}, {"source": 765, "target": 291, "value": 8}, {"source": 765, "target": 293, "value": 10}, {"source": 765, "target": 315, "value": 4}, {"source": 765, "target": 317, "value": 10}, {"source": 765, "target": 321, "value": 8}, {"source": 765, "target": 341, "value": 8}, {"source": 765, "target": 353, "value": 9}, {"source": 765, "target": 365, "value": 1}, {"source": 765, "target": 375, "value": 8}, {"source": 765, "target": 377, "value": 8}, {"source": 765, "target": 378, "value": 8}, {"source": 765, "target": 405, "value": 10}, {"source": 765, "target": 413, "value": 8}, {"source": 765, "target": 425, "value": 8}, {"source": 765, "target": 428, "value": 8}, {"source": 765, "target": 441, "value": 8}, {"source": 765, "target": 453, "value": 4}, {"source": 765, "target": 461, "value": 8}, {"source": 765, "target": 465, "value": 2}, {"source": 765, "target": 491, "value": 8}, {"source": 765, "target": 513, "value": 8}, {"source": 765, "target": 515, "value": 4}, {"source": 765, "target": 525, "value": 8}, {"source": 765, "target": 527, "value": 8}, {"source": 765, "target": 528, "value": 8}, {"source": 765, "target": 563, "value": 8}, {"source": 765, "target": 573, "value": 8}, {"source": 765, "target": 575, "value": 8}, {"source": 765, "target": 578, "value": 8}, {"source": 765, "target": 591, "value": 8}, {"source": 765, "target": 605, "value": 9}, {"source": 765, "target": 615, "value": 4}, {"source": 765, "target": 641, "value": 8}, {"source": 765, "target": 653, "value": 8}, {"source": 765, "target": 665, "value": 2}, {"source": 765, "target": 671, "value": 8}, {"source": 765, "target": 675, "value": 8}, {"source": 765, "target": 677, "value": 7}, {"source": 765, "target": 678, "value": 8}, {"source": 765, "target": 713, "value": 4}, {"source": 765, "target": 725, "value": 8}, {"source": 765, "target": 728, "value": 8}, {"source": 765, "target": 741, "value": 8}, {"source": 765, "target": 753, "value": 7}, {"source": 767, "target": 225, "value": 8}, {"source": 767, "target": 228, "value": 8}, {"source": 767, "target": 243, "value": 9}, {"source": 767, "target": 255, "value": 10}, {"source": 767, "target": 275, "value": 8}, {"source": 767, "target": 278, "value": 8}, {"source": 767, "target": 293, "value": 4}, {"source": 767, "target": 305, "value": 9}, {"source": 767, "target": 317, "value": 4}, {"source": 767, "target": 321, "value": 8}, {"source": 767, "target": 363, "value": 8}, {"source": 767, "target": 365, "value": 9}, {"source": 767, "target": 375, "value": 8}, {"source": 767, "target": 378, "value": 8}, {"source": 767, "target": 393, "value": 4}, {"source": 767, "target": 405, "value": 8}, {"source": 767, "target": 413, "value": 8}, {"source": 767, "target": 425, "value": 8}, {"source": 767, "target": 428, "value": 8}, {"source": 767, "target": 443, "value": 9}, {"source": 767, "target": 467, "value": 4}, {"source": 767, "target": 489, "value": 10}, {"source": 767, "target": 513, "value": 8}, {"source": 767, "target": 525, "value": 8}, {"source": 767, "target": 528, "value": 8}, {"source": 767, "target": 543, "value": 8}, {"source": 767, "target": 557, "value": 9}, {"source": 767, "target": 563, "value": 8}, {"source": 767, "target": 575, "value": 8}, {"source": 767, "target": 578, "value": 8}, {"source": 767, "target": 593, "value": 4}, {"source": 767, "target": 605, "value": 8}, {"source": 767, "target": 617, "value": 4}, {"source": 767, "target": 671, "value": 8}, {"source": 767, "target": 675, "value": 8}, {"source": 767, "target": 678, "value": 8}, {"source": 767, "target": 693, "value": 4}, {"source": 767, "target": 705, "value": 7}, {"source": 767, "target": 713, "value": 4}, {"source": 767, "target": 725, "value": 8}, {"source": 767, "target": 728, "value": 8}, {"source": 767, "target": 743, "value": 8}, {"source": 767, "target": 765, "value": 7}, {"source": 771, "target": 221, "value": 4}, {"source": 771, "target": 261, "value": 11}, {"source": 771, "target": 293, "value": 12}, {"source": 771, "target": 309, "value": 9}, {"source": 771, "target": 321, "value": 4}, {"source": 771, "target": 371, "value": 4}, {"source": 771, "target": 461, "value": 10}, {"source": 771, "target": 471, "value": 4}, {"source": 771, "target": 521, "value": 4}, {"source": 771, "target": 573, "value": 9}, {"source": 771, "target": 609, "value": 8}, {"source": 771, "target": 621, "value": 4}, {"source": 771, "target": 663, "value": 9}, {"source": 771, "target": 671, "value": 4}, {"source": 773, "target": 219, "value": 8}, {"source": 773, "target": 221, "value": 2}, {"source": 773, "target": 233, "value": 8}, {"source": 773, "target": 248, "value": 8}, {"source": 773, "target": 257, "value": 8}, {"source": 773, "target": 261, "value": 9}, {"source": 773, "target": 273, "value": 1}, {"source": 773, "target": 279, "value": 8}, {"source": 773, "target": 285, "value": 8}, {"source": 773, "target": 309, "value": 9}, {"source": 773, "target": 321, "value": 4}, {"source": 773, "target": 323, "value": 1}, {"source": 773, "target": 333, "value": 8}, {"source": 773, "target": 335, "value": 8}, {"source": 773, "target": 347, "value": 4}, {"source": 773, "target": 348, "value": 8}, {"source": 773, "target": 369, "value": 8}, {"source": 773, "target": 377, "value": 10}, {"source": 773, "target": 383, "value": 8}, {"source": 773, "target": 398, "value": 8}, {"source": 773, "target": 413, "value": 10}, {"source": 773, "target": 423, "value": 1}, {"source": 773, "target": 435, "value": 8}, {"source": 773, "target": 461, "value": 8}, {"source": 773, "target": 473, "value": 1}, {"source": 773, "target": 483, "value": 8}, {"source": 773, "target": 485, "value": 8}, {"source": 773, "target": 497, "value": 4}, {"source": 773, "target": 498, "value": 8}, {"source": 773, "target": 519, "value": 8}, {"source": 773, "target": 521, "value": 8}, {"source": 773, "target": 525, "value": 9}, {"source": 773, "target": 531, "value": 8}, {"source": 773, "target": 533, "value": 8}, {"source": 773, "target": 545, "value": 10}, {"source": 773, "target": 548, "value": 8}, {"source": 773, "target": 557, "value": 8}, {"source": 773, "target": 561, "value": 8}, {"source": 773, "target": 573, "value": 0}, {"source": 773, "target": 585, "value": 8}, {"source": 773, "target": 611, "value": 8}, {"source": 773, "target": 615, "value": 8}, {"source": 773, "target": 621, "value": 2}, {"source": 773, "target": 623, "value": 1}, {"source": 773, "target": 633, "value": 8}, {"source": 773, "target": 635, "value": 8}, {"source": 773, "target": 647, "value": 4}, {"source": 773, "target": 648, "value": 8}, {"source": 773, "target": 669, "value": 8}, {"source": 773, "target": 678, "value": 8}, {"source": 773, "target": 683, "value": 8}, {"source": 773, "target": 698, "value": 7}, {"source": 773, "target": 699, "value": 2}, {"source": 773, "target": 723, "value": 1}, {"source": 773, "target": 725, "value": 8}, {"source": 773, "target": 735, "value": 8}, {"source": 773, "target": 761, "value": 4}, {"source": 783, "target": 221, "value": 4}, {"source": 783, "target": 225, "value": 9}, {"source": 783, "target": 231, "value": 8}, {"source": 783, "target": 233, "value": 1}, {"source": 783, "target": 245, "value": 8}, {"source": 783, "target": 248, "value": 8}, {"source": 783, "target": 257, "value": 8}, {"source": 783, "target": 279, "value": 4}, {"source": 783, "target": 281, "value": 8}, {"source": 783, "target": 285, "value": 10}, {"source": 783, "target": 287, "value": 8}, {"source": 783, "target": 317, "value": 8}, {"source": 783, "target": 321, "value": 4}, {"source": 783, "target": 333, "value": 1}, {"source": 783, "target": 335, "value": 10}, {"source": 783, "target": 345, "value": 8}, {"source": 783, "target": 348, "value": 8}, {"source": 783, "target": 381, "value": 4}, {"source": 783, "target": 383, "value": 2}, {"source": 783, "target": 395, "value": 8}, {"source": 783, "target": 398, "value": 8}, {"source": 783, "target": 405, "value": 11}, {"source": 783, "target": 407, "value": 8}, {"source": 783, "target": 425, "value": 8}, {"source": 783, "target": 429, "value": 4}, {"source": 783, "target": 431, "value": 8}, {"source": 783, "target": 437, "value": 8}, {"source": 783, "target": 483, "value": 2}, {"source": 783, "target": 485, "value": 9}, {"source": 783, "target": 495, "value": 8}, {"source": 783, "target": 498, "value": 8}, {"source": 783, "target": 521, "value": 8}, {"source": 783, "target": 525, "value": 8}, {"source": 783, "target": 531, "value": 8}, {"source": 783, "target": 533, "value": 1}, {"source": 783, "target": 545, "value": 8}, {"source": 783, "target": 548, "value": 8}, {"source": 783, "target": 557, "value": 4}, {"source": 783, "target": 573, "value": 9}, {"source": 783, "target": 579, "value": 8}, {"source": 783, "target": 581, "value": 8}, {"source": 783, "target": 587, "value": 8}, {"source": 783, "target": 605, "value": 8}, {"source": 783, "target": 617, "value": 8}, {"source": 783, "target": 621, "value": 8}, {"source": 783, "target": 633, "value": 1}, {"source": 783, "target": 645, "value": 8}, {"source": 783, "target": 648, "value": 8}, {"source": 783, "target": 669, "value": 8}, {"source": 783, "target": 677, "value": 8}, {"source": 783, "target": 681, "value": 8}, {"source": 783, "target": 683, "value": 2}, {"source": 783, "target": 695, "value": 8}, {"source": 783, "target": 698, "value": 8}, {"source": 783, "target": 707, "value": 8}, {"source": 783, "target": 725, "value": 8}, {"source": 783, "target": 729, "value": 8}, {"source": 783, "target": 731, "value": 7}, {"source": 783, "target": 737, "value": 7}, {"source": 783, "target": 741, "value": 8}, {"source": 783, "target": 773, "value": 7}, {"source": 785, "target": 273, "value": 9}, {"source": 785, "target": 285, "value": 2}, {"source": 785, "target": 293, "value": 8}, {"source": 785, "target": 335, "value": 4}, {"source": 785, "target": 347, "value": 8}, {"source": 785, "target": 377, "value": 10}, {"source": 785, "target": 435, "value": 4}, {"source": 785, "target": 437, "value": 9}, {"source": 785, "target": 473, "value": 8}, {"source": 785, "target": 485, "value": 2}, {"source": 785, "target": 497, "value": 8}, {"source": 785, "target": 525, "value": 9}, {"source": 785, "target": 531, "value": 8}, {"source": 785, "target": 573, "value": 8}, {"source": 785, "target": 585, "value": 2}, {"source": 785, "target": 587, "value": 9}, {"source": 785, "target": 635, "value": 4}, {"source": 785, "target": 647, "value": 8}, {"source": 785, "target": 725, "value": 8}, {"source": 785, "target": 735, "value": 3}, {"source": 785, "target": 773, "value": 7}, {"source": 789, "target": 228, "value": 8}, {"source": 789, "target": 243, "value": 12}, {"source": 789, "target": 249, "value": 8}, {"source": 789, "target": 251, "value": 8}, {"source": 789, "target": 278, "value": 8}, {"source": 789, "target": 293, "value": 12}, {"source": 789, "target": 303, "value": 10}, {"source": 789, "target": 335, "value": 8}, {"source": 789, "target": 339, "value": 4}, {"source": 789, "target": 341, "value": 10}, {"source": 789, "target": 351, "value": 8}, {"source": 789, "target": 353, "value": 10}, {"source": 789, "target": 377, "value": 8}, {"source": 789, "target": 378, "value": 8}, {"source": 789, "target": 393, "value": 11}, {"source": 789, "target": 399, "value": 8}, {"source": 789, "target": 401, "value": 8}, {"source": 789, "target": 413, "value": 8}, {"source": 789, "target": 428, "value": 8}, {"source": 789, "target": 429, "value": 10}, {"source": 789, "target": 437, "value": 8}, {"source": 789, "target": 443, "value": 10}, {"source": 789, "target": 453, "value": 9}, {"source": 789, "target": 461, "value": 10}, {"source": 789, "target": 489, "value": 2}, {"source": 789, "target": 501, "value": 8}, {"source": 789, "target": 503, "value": 9}, {"source": 789, "target": 528, "value": 8}, {"source": 789, "target": 543, "value": 10}, {"source": 789, "target": 545, "value": 8}, {"source": 789, "target": 549, "value": 8}, {"source": 789, "target": 551, "value": 8}, {"source": 789, "target": 578, "value": 8}, {"source": 789, "target": 593, "value": 9}, {"source": 789, "target": 603, "value": 8}, {"source": 789, "target": 639, "value": 4}, {"source": 789, "target": 651, "value": 8}, {"source": 789, "target": 653, "value": 8}, {"source": 789, "target": 677, "value": 8}, {"source": 789, "target": 678, "value": 7}, {"source": 789, "target": 693, "value": 9}, {"source": 789, "target": 699, "value": 8}, {"source": 789, "target": 701, "value": 7}, {"source": 789, "target": 728, "value": 7}, {"source": 789, "target": 737, "value": 7}, {"source": 789, "target": 741, "value": 2}, {"source": 789, "target": 743, "value": 8}, {"source": 789, "target": 753, "value": 8}, {"source": 791, "target": 291, "value": 4}, {"source": 791, "target": 293, "value": 10}, {"source": 791, "target": 341, "value": 4}, {"source": 791, "target": 365, "value": 8}, {"source": 791, "target": 381, "value": 10}, {"source": 791, "target": 429, "value": 8}, {"source": 791, "target": 441, "value": 4}, {"source": 791, "target": 491, "value": 4}, {"source": 791, "target": 533, "value": 9}, {"source": 791, "target": 581, "value": 9}, {"source": 791, "target": 587, "value": 8}, {"source": 791, "target": 591, "value": 4}, {"source": 791, "target": 641, "value": 4}, {"source": 791, "target": 713, "value": 9}, {"source": 791, "target": 729, "value": 7}, {"source": 791, "target": 741, "value": 3}, {"source": 791, "target": 765, "value": 8}, {"source": 795, "target": 233, "value": 4}, {"source": 795, "target": 245, "value": 4}, {"source": 795, "target": 248, "value": 8}, {"source": 795, "target": 285, "value": 10}, {"source": 795, "target": 333, "value": 9}, {"source": 795, "target": 345, "value": 4}, {"source": 795, "target": 348, "value": 8}, {"source": 795, "target": 395, "value": 4}, {"source": 795, "target": 398, "value": 8}, {"source": 795, "target": 485, "value": 9}, {"source": 795, "target": 495, "value": 4}, {"source": 795, "target": 498, "value": 8}, {"source": 795, "target": 533, "value": 8}, {"source": 795, "target": 545, "value": 4}, {"source": 795, "target": 548, "value": 8}, {"source": 795, "target": 609, "value": 3}, {"source": 795, "target": 633, "value": 8}, {"source": 795, "target": 645, "value": 4}, {"source": 795, "target": 648, "value": 7}, {"source": 795, "target": 669, "value": 8}, {"source": 795, "target": 695, "value": 4}, {"source": 795, "target": 698, "value": 7}, {"source": 795, "target": 741, "value": 7}, {"source": 795, "target": 783, "value": 8}, {"source": 797, "target": 221, "value": 9}, {"source": 797, "target": 225, "value": 8}, {"source": 797, "target": 245, "value": 4}, {"source": 797, "target": 251, "value": 8}, {"source": 797, "target": 257, "value": 8}, {"source": 797, "target": 263, "value": 12}, {"source": 797, "target": 273, "value": 3}, {"source": 797, "target": 275, "value": 8}, {"source": 797, "target": 285, "value": 9}, {"source": 797, "target": 293, "value": 8}, {"source": 797, "target": 309, "value": 8}, {"source": 797, "target": 323, "value": 4}, {"source": 797, "target": 341, "value": 10}, {"source": 797, "target": 345, "value": 4}, {"source": 797, "target": 347, "value": 4}, {"source": 797, "target": 351, "value": 4}, {"source": 797, "target": 363, "value": 11}, {"source": 797, "target": 375, "value": 8}, {"source": 797, "target": 377, "value": 9}, {"source": 797, "target": 395, "value": 8}, {"source": 797, "target": 401, "value": 8}, {"source": 797, "target": 405, "value": 8}, {"source": 797, "target": 407, "value": 8}, {"source": 797, "target": 413, "value": 10}, {"source": 797, "target": 423, "value": 4}, {"source": 797, "target": 425, "value": 8}, {"source": 797, "target": 437, "value": 10}, {"source": 797, "target": 459, "value": 8}, {"source": 797, "target": 473, "value": 2}, {"source": 797, "target": 485, "value": 8}, {"source": 797, "target": 489, "value": 8}, {"source": 797, "target": 495, "value": 8}, {"source": 797, "target": 497, "value": 2}, {"source": 797, "target": 501, "value": 8}, {"source": 797, "target": 503, "value": 8}, {"source": 797, "target": 513, "value": 10}, {"source": 797, "target": 525, "value": 8}, {"source": 797, "target": 545, "value": 2}, {"source": 797, "target": 549, "value": 9}, {"source": 797, "target": 551, "value": 8}, {"source": 797, "target": 557, "value": 8}, {"source": 797, "target": 563, "value": 9}, {"source": 797, "target": 573, "value": 2}, {"source": 797, "target": 575, "value": 8}, {"source": 797, "target": 585, "value": 4}, {"source": 797, "target": 609, "value": 8}, {"source": 797, "target": 621, "value": 8}, {"source": 797, "target": 623, "value": 4}, {"source": 797, "target": 645, "value": 4}, {"source": 797, "target": 647, "value": 4}, {"source": 797, "target": 651, "value": 8}, {"source": 797, "target": 663, "value": 9}, {"source": 797, "target": 669, "value": 7}, {"source": 797, "target": 675, "value": 8}, {"source": 797, "target": 695, "value": 4}, {"source": 797, "target": 699, "value": 8}, {"source": 797, "target": 701, "value": 8}, {"source": 797, "target": 707, "value": 8}, {"source": 797, "target": 713, "value": 8}, {"source": 797, "target": 723, "value": 4}, {"source": 797, "target": 725, "value": 8}, {"source": 797, "target": 737, "value": 3}, {"source": 797, "target": 741, "value": 8}, {"source": 797, "target": 755, "value": 7}, {"source": 797, "target": 759, "value": 8}, {"source": 797, "target": 773, "value": 2}, {"source": 797, "target": 785, "value": 7}, {"source": 797, "target": 789, "value": 7}, {"source": 797, "target": 795, "value": 7}, {"source": 798, "target": 219, "value": 8}, {"source": 798, "target": 233, "value": 5}, {"source": 798, "target": 245, "value": 8}, {"source": 798, "target": 248, "value": 4}, {"source": 798, "target": 257, "value": 8}, {"source": 798, "target": 273, "value": 11}, {"source": 798, "target": 285, "value": 11}, {"source": 798, "target": 309, "value": 10}, {"source": 798, "target": 323, "value": 11}, {"source": 798, "target": 333, "value": 9}, {"source": 798, "target": 345, "value": 8}, {"source": 798, "target": 348, "value": 4}, {"source": 798, "target": 369, "value": 8}, {"source": 798, "target": 395, "value": 8}, {"source": 798, "target": 398, "value": 4}, {"source": 798, "target": 423, "value": 10}, {"source": 798, "target": 473, "value": 10}, {"source": 798, "target": 485, "value": 10}, {"source": 798, "target": 495, "value": 8}, {"source": 798, "target": 498, "value": 4}, {"source": 798, "target": 519, "value": 8}, {"source": 798, "target": 533, "value": 8}, {"source": 798, "target": 545, "value": 8}, {"source": 798, "target": 548, "value": 4}, {"source": 798, "target": 557, "value": 8}, {"source": 798, "target": 573, "value": 9}, {"source": 798, "target": 611, "value": 9}, {"source": 798, "target": 615, "value": 8}, {"source": 798, "target": 623, "value": 9}, {"source": 798, "target": 633, "value": 8}, {"source": 798, "target": 645, "value": 8}, {"source": 798, "target": 648, "value": 4}, {"source": 798, "target": 669, "value": 8}, {"source": 798, "target": 695, "value": 8}, {"source": 798, "target": 698, "value": 4}, {"source": 798, "target": 723, "value": 8}, {"source": 798, "target": 741, "value": 7}, {"source": 798, "target": 773, "value": 8}, {"source": 798, "target": 783, "value": 8}, {"source": 798, "target": 795, "value": 8}, {"source": 801, "target": 251, "value": 4}, {"source": 801, "target": 263, "value": 8}, {"source": 801, "target": 341, "value": 10}, {"source": 801, "target": 351, "value": 4}, {"source": 801, "target": 363, "value": 8}, {"source": 801, "target": 401, "value": 2}, {"source": 801, "target": 413, "value": 8}, {"source": 801, "target": 453, "value": 9}, {"source": 801, "target": 455, "value": 10}, {"source": 801, "target": 489, "value": 8}, {"source": 801, "target": 501, "value": 2}, {"source": 801, "target": 503, "value": 9}, {"source": 801, "target": 513, "value": 8}, {"source": 801, "target": 525, "value": 8}, {"source": 801, "target": 551, "value": 4}, {"source": 801, "target": 563, "value": 8}, {"source": 801, "target": 573, "value": 10}, {"source": 801, "target": 651, "value": 2}, {"source": 801, "target": 653, "value": 8}, {"source": 801, "target": 663, "value": 8}, {"source": 801, "target": 701, "value": 2}, {"source": 801, "target": 713, "value": 2}, {"source": 801, "target": 741, "value": 8}, {"source": 801, "target": 783, "value": 3}, {"source": 801, "target": 789, "value": 7}, {"source": 801, "target": 797, "value": 8}, {"source": 803, "target": 227, "value": 8}, {"source": 803, "target": 228, "value": 8}, {"source": 803, "target": 249, "value": 8}, {"source": 803, "target": 278, "value": 8}, {"source": 803, "target": 279, "value": 10}, {"source": 803, "target": 293, "value": 10}, {"source": 803, "target": 303, "value": 2}, {"source": 803, "target": 341, "value": 9}, {"source": 803, "target": 353, "value": 1}, {"source": 803, "target": 377, "value": 8}, {"source": 803, "target": 378, "value": 8}, {"source": 803, "target": 399, "value": 8}, {"source": 803, "target": 405, "value": 10}, {"source": 803, "target": 428, "value": 8}, {"source": 803, "target": 437, "value": 8}, {"source": 803, "target": 441, "value": 8}, {"source": 803, "target": 453, "value": 1}, {"source": 803, "target": 503, "value": 2}, {"source": 803, "target": 527, "value": 8}, {"source": 803, "target": 528, "value": 8}, {"source": 803, "target": 545, "value": 8}, {"source": 803, "target": 549, "value": 8}, {"source": 803, "target": 578, "value": 8}, {"source": 803, "target": 603, "value": 2}, {"source": 803, "target": 641, "value": 8}, {"source": 803, "target": 653, "value": 1}, {"source": 803, "target": 677, "value": 8}, {"source": 803, "target": 678, "value": 8}, {"source": 803, "target": 693, "value": 8}, {"source": 803, "target": 699, "value": 4}, {"source": 803, "target": 701, "value": 8}, {"source": 803, "target": 728, "value": 7}, {"source": 803, "target": 737, "value": 8}, {"source": 803, "target": 741, "value": 3}, {"source": 803, "target": 753, "value": 1}, {"source": 803, "target": 755, "value": 8}, {"source": 803, "target": 789, "value": 7}]}
\ No newline at end of file
diff --git a/galaxy/tests.py b/galaxy/tests.py
index 70314574..cfbaf5ca 100644
--- a/galaxy/tests.py
+++ b/galaxy/tests.py
@@ -23,9 +23,9 @@
#
import json
-
from pathlib import Path
+import pytest
from django.core.management import call_command
from django.test import TestCase
from django.urls import reverse
@@ -35,29 +35,30 @@ from galaxy.models import Galaxy
class GalaxyTestModel(TestCase):
- def setUp(self):
- self.root = User.objects.get(username="root")
- self.skia = User.objects.get(username="skia")
- self.sli = User.objects.get(username="sli")
- self.krophil = User.objects.get(username="krophil")
- self.richard = User.objects.get(username="rbatsbak")
- self.subscriber = User.objects.get(username="subscriber")
- self.public = User.objects.get(username="public")
- self.com = User.objects.get(username="comunity")
+ @classmethod
+ def setUpTestData(cls):
+ cls.root = User.objects.get(username="root")
+ cls.skia = User.objects.get(username="skia")
+ cls.sli = User.objects.get(username="sli")
+ cls.krophil = User.objects.get(username="krophil")
+ cls.richard = User.objects.get(username="rbatsbak")
+ cls.subscriber = User.objects.get(username="subscriber")
+ cls.public = User.objects.get(username="public")
+ cls.com = User.objects.get(username="comunity")
def test_user_self_score(self):
"""
Test that individual user scores are correct
"""
with self.assertNumQueries(8):
- self.assertEqual(Galaxy.compute_user_score(self.root), 9)
- self.assertEqual(Galaxy.compute_user_score(self.skia), 10)
- self.assertEqual(Galaxy.compute_user_score(self.sli), 8)
- self.assertEqual(Galaxy.compute_user_score(self.krophil), 2)
- self.assertEqual(Galaxy.compute_user_score(self.richard), 10)
- self.assertEqual(Galaxy.compute_user_score(self.subscriber), 8)
- self.assertEqual(Galaxy.compute_user_score(self.public), 8)
- self.assertEqual(Galaxy.compute_user_score(self.com), 1)
+ assert Galaxy.compute_user_score(self.root) == 9
+ assert Galaxy.compute_user_score(self.skia) == 10
+ assert Galaxy.compute_user_score(self.sli) == 8
+ assert Galaxy.compute_user_score(self.krophil) == 2
+ assert Galaxy.compute_user_score(self.richard) == 10
+ assert Galaxy.compute_user_score(self.subscriber) == 8
+ assert Galaxy.compute_user_score(self.public) == 8
+ assert Galaxy.compute_user_score(self.com) == 1
def test_users_score(self):
"""
@@ -147,6 +148,7 @@ class GalaxyTestModel(TestCase):
galaxy.rule(0) # We want everybody here
+@pytest.mark.slow
class GalaxyTestView(TestCase):
@classmethod
def setUpTestData(cls):
@@ -156,12 +158,13 @@ class GalaxyTestView(TestCase):
call_command("generate_galaxy_test_data", "-v", "0")
galaxy = Galaxy.objects.create()
galaxy.rule(26) # We want a fast test
+ cls.root = User.objects.get(username="root")
def test_page_is_citizen(self):
"""
Test that users can access the galaxy page of users who are citizens
"""
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
user = User.objects.get(last_name="n°500")
response = self.client.get(reverse("galaxy:user", args=[user.id]))
self.assertContains(
@@ -175,10 +178,10 @@ class GalaxyTestView(TestCase):
Test that trying to access the galaxy page of a user who is not
citizens return a 404
"""
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
user = User.objects.get(last_name="n°1")
response = self.client.get(reverse("galaxy:user", args=[user.id]))
- self.assertEquals(response.status_code, 404)
+ assert response.status_code == 404
def test_full_galaxy_state(self):
"""
@@ -186,7 +189,7 @@ class GalaxyTestView(TestCase):
command that the relation scores are correct, and that the view exposes the
right data.
"""
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
response = self.client.get(reverse("galaxy:data"))
state = response.json()
@@ -195,7 +198,4 @@ class GalaxyTestView(TestCase):
# Dump computed state, either for easier debugging, or to copy as new reference if changes are legit
(galaxy_dir / "test_galaxy_state.json").write_text(json.dumps(state))
- self.assertEqual(
- state,
- json.loads((galaxy_dir / "ref_galaxy_state.json").read_text()),
- )
+ assert state == json.loads((galaxy_dir / "ref_galaxy_state.json").read_text())
diff --git a/galaxy/views.py b/galaxy/views.py
index 3dda003e..3e0b33da 100644
--- a/galaxy/views.py
+++ b/galaxy/views.py
@@ -22,18 +22,18 @@
#
#
-from django.views.generic import DetailView, View
-from django.http import JsonResponse, Http404
-from django.db.models import Q, Case, F, When, Value
+from django.db.models import Case, F, Q, Value, When
from django.db.models.functions import Concat
+from django.http import Http404, JsonResponse
from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, View
+from core.models import User
from core.views import (
CanViewMixin,
FormerSubscriberMixin,
+ UserTabsMixin,
)
-from core.models import User
-from core.views import UserTabsMixin
from galaxy.models import Galaxy, GalaxyLane
diff --git a/launderette/migrations/0001_initial.py b/launderette/migrations/0001_initial.py
index 79f011a7..f1548cd2 100644
--- a/launderette/migrations/0001_initial.py
+++ b/launderette/migrations/0001_initial.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/launderette/models.py b/launderette/models.py
index 3ca12b2d..c2f344bb 100644
--- a/launderette/models.py
+++ b/launderette/models.py
@@ -14,14 +14,14 @@
#
#
-from django.db import models, DataError
-from django.utils.translation import gettext_lazy as _
from django.conf import settings
+from django.db import DataError, models
from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
-from counter.models import Counter
-from core.models import User
from club.models import Club
+from core.models import User
+from counter.models import Counter
# Create your models here.
diff --git a/launderette/tests.py b/launderette/tests.py
index 46a200c2..d888e761 100644
--- a/launderette/tests.py
+++ b/launderette/tests.py
@@ -14,6 +14,4 @@
#
#
-from django.test import TestCase
-
# Create your tests here.
diff --git a/launderette/views.py b/launderette/views.py
index 716b41f4..c0b16eed 100644
--- a/launderette/views.py
+++ b/launderette/views.py
@@ -14,27 +14,26 @@
#
#
-from datetime import datetime, timedelta
from collections import OrderedDict
+from datetime import datetime, timedelta
+
import pytz
-
-from django.views.generic import ListView, DetailView, TemplateView
-from django.views.generic.edit import UpdateView, CreateView, DeleteView, BaseFormView
-from django.utils.translation import gettext as _
-from django.utils import dateparse, timezone
-from django.urls import reverse_lazy
-from django.conf import settings
-from django.db import transaction, DataError
from django import forms
+from django.conf import settings
+from django.db import DataError, transaction
from django.template import defaultfilters
+from django.urls import reverse_lazy
+from django.utils import dateparse, timezone
+from django.utils.translation import gettext as _
+from django.views.generic import DetailView, ListView, TemplateView
+from django.views.generic.edit import BaseFormView, CreateView, DeleteView, UpdateView
-from core.models import Page, User
from club.models import Club
-from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
-from launderette.models import Launderette, Token, Machine, Slot
-from counter.models import Counter, Customer, Selling
+from core.models import Page, User
+from core.views import CanCreateMixin, CanEditMixin, CanEditPropMixin, CanViewMixin
from counter.forms import GetUserForm
-
+from counter.models import Counter, Customer, Selling
+from launderette.models import Launderette, Machine, Slot, Token
# For users
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index fd44743c..884375a2 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-03-07 23:04+0100\n"
+"POT-Creation-Date: 2024-07-04 10:44+0200\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Skia \n"
"Language-Team: AE info \n"
@@ -16,178 +16,178 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: accounting/models.py:61 accounting/models.py:110 accounting/models.py:143
-#: accounting/models.py:216 club/models.py:48 com/models.py:279
-#: com/models.py:296 counter/models.py:222 counter/models.py:255
-#: counter/models.py:369 forum/models.py:58 launderette/models.py:38
-#: launderette/models.py:93 launderette/models.py:131 stock/models.py:40
-#: stock/models.py:63 stock/models.py:105 stock/models.py:133
+#: accounting/models.py:53 accounting/models.py:102 accounting/models.py:137
+#: accounting/models.py:212 club/models.py:50 com/models.py:287
+#: com/models.py:306 counter/models.py:212 counter/models.py:247
+#: counter/models.py:363 forum/models.py:58 launderette/models.py:30
+#: launderette/models.py:87 launderette/models.py:127 stock/models.py:39
+#: stock/models.py:62 stock/models.py:104 stock/models.py:132
msgid "name"
msgstr "nom"
-#: accounting/models.py:62
+#: accounting/models.py:54
msgid "street"
msgstr "rue"
-#: accounting/models.py:63
+#: accounting/models.py:55
msgid "city"
msgstr "ville"
-#: accounting/models.py:64
+#: accounting/models.py:56
msgid "postcode"
msgstr "code postal"
-#: accounting/models.py:65
+#: accounting/models.py:57
msgid "country"
msgstr "pays"
-#: accounting/models.py:66 core/models.py:283
+#: accounting/models.py:58 core/models.py:361
msgid "phone"
msgstr "téléphone"
-#: accounting/models.py:67
+#: accounting/models.py:59
msgid "email"
msgstr "email"
-#: accounting/models.py:68
+#: accounting/models.py:60
msgid "website"
msgstr "site internet"
-#: accounting/models.py:71
+#: accounting/models.py:63
msgid "company"
msgstr "entreprise"
-#: accounting/models.py:111
+#: accounting/models.py:103
msgid "iban"
msgstr "IBAN"
-#: accounting/models.py:112
+#: accounting/models.py:104
msgid "account number"
msgstr "numéro de compte"
-#: accounting/models.py:116 accounting/models.py:147 club/models.py:275
-#: com/models.py:75 com/models.py:266 com/models.py:302 counter/models.py:273
-#: counter/models.py:371 trombi/models.py:217
+#: accounting/models.py:108 accounting/models.py:141 club/models.py:356
+#: com/models.py:77 com/models.py:272 com/models.py:312 counter/models.py:265
+#: counter/models.py:365 trombi/models.py:217
msgid "club"
msgstr "club"
-#: accounting/models.py:121
+#: accounting/models.py:113
msgid "Bank account"
msgstr "Compte en banque"
-#: accounting/models.py:153
+#: accounting/models.py:147
msgid "bank account"
msgstr "compte en banque"
-#: accounting/models.py:158
+#: accounting/models.py:152
msgid "Club account"
msgstr "Compte club"
-#: accounting/models.py:203
+#: accounting/models.py:199
#, python-format
msgid "%(club_account)s on %(bank_account)s"
msgstr "%(club_account)s sur %(bank_account)s"
-#: accounting/models.py:214 club/models.py:281 counter/models.py:878
-#: election/models.py:18 launderette/models.py:194
+#: accounting/models.py:210 club/models.py:362 counter/models.py:883
+#: election/models.py:18 launderette/models.py:192
msgid "start date"
msgstr "date de début"
-#: accounting/models.py:215 club/models.py:282 counter/models.py:879
+#: accounting/models.py:211 club/models.py:363 counter/models.py:884
#: election/models.py:19
msgid "end date"
msgstr "date de fin"
-#: accounting/models.py:217
+#: accounting/models.py:213
msgid "is closed"
msgstr "est fermé"
-#: accounting/models.py:222 accounting/models.py:549
+#: accounting/models.py:218 accounting/models.py:551
msgid "club account"
msgstr "compte club"
-#: accounting/models.py:225 accounting/models.py:289 counter/models.py:64
-#: counter/models.py:600
+#: accounting/models.py:221 accounting/models.py:287 counter/models.py:54
+#: counter/models.py:601
msgid "amount"
msgstr "montant"
-#: accounting/models.py:226
+#: accounting/models.py:222
msgid "effective_amount"
msgstr "montant effectif"
-#: accounting/models.py:229
+#: accounting/models.py:225
msgid "General journal"
msgstr "Classeur"
-#: accounting/models.py:281
+#: accounting/models.py:279
msgid "number"
msgstr "numéro"
-#: accounting/models.py:286
+#: accounting/models.py:284
msgid "journal"
msgstr "classeur"
-#: accounting/models.py:290 core/models.py:862 core/models.py:1400
-#: core/models.py:1448 core/models.py:1477 core/models.py:1501
-#: counter/models.py:610 counter/models.py:703 counter/models.py:914
-#: eboutic/models.py:67 eboutic/models.py:236 forum/models.py:311
-#: forum/models.py:408 stock/models.py:104
+#: accounting/models.py:288 core/models.py:914 core/models.py:1477
+#: core/models.py:1525 core/models.py:1554 core/models.py:1580
+#: counter/models.py:611 counter/models.py:706 counter/models.py:919
+#: eboutic/models.py:58 eboutic/models.py:227 forum/models.py:314
+#: forum/models.py:414 stock/models.py:103
msgid "date"
msgstr "date"
-#: accounting/models.py:291 counter/models.py:224 counter/models.py:915
-#: pedagogy/models.py:219 stock/models.py:107
+#: accounting/models.py:289 counter/models.py:214 counter/models.py:920
+#: pedagogy/models.py:218 stock/models.py:106
msgid "comment"
msgstr "commentaire"
-#: accounting/models.py:293 counter/models.py:612 counter/models.py:705
-#: subscription/models.py:65
+#: accounting/models.py:291 counter/models.py:613 counter/models.py:708
+#: subscription/models.py:56
msgid "payment method"
msgstr "méthode de paiement"
-#: accounting/models.py:298
+#: accounting/models.py:296
msgid "cheque number"
msgstr "numéro de chèque"
-#: accounting/models.py:303 eboutic/models.py:329
+#: accounting/models.py:301 eboutic/models.py:320
msgid "invoice"
msgstr "facture"
-#: accounting/models.py:308
+#: accounting/models.py:306
msgid "is done"
msgstr "est fait"
-#: accounting/models.py:312
+#: accounting/models.py:310
msgid "simple type"
msgstr "type simplifié"
-#: accounting/models.py:320 accounting/models.py:487
+#: accounting/models.py:318 accounting/models.py:487
msgid "accounting type"
msgstr "type comptable"
-#: accounting/models.py:328 accounting/models.py:475 accounting/models.py:510
-#: accounting/models.py:545 core/models.py:1476 core/models.py:1502
-#: counter/models.py:669
+#: accounting/models.py:326 accounting/models.py:475 accounting/models.py:512
+#: accounting/models.py:547 core/models.py:1553 core/models.py:1581
+#: counter/models.py:672
msgid "label"
msgstr "étiquette"
-#: accounting/models.py:334
+#: accounting/models.py:332
msgid "target type"
msgstr "type de cible"
-#: accounting/models.py:337 club/models.py:438
-#: club/templates/club/club_members.jinja:16
+#: accounting/models.py:335 club/models.py:528
+#: club/templates/club/club_members.jinja:17
#: club/templates/club/club_old_members.jinja:8
#: club/templates/club/mailing.jinja:41
#: counter/templates/counter/cash_summary_list.jinja:32
-#: counter/templates/counter/stats.jinja:19
-#: counter/templates/counter/stats.jinja:43
-#: counter/templates/counter/stats.jinja:65
+#: counter/templates/counter/stats.jinja:21
+#: counter/templates/counter/stats.jinja:47
+#: counter/templates/counter/stats.jinja:69
#: launderette/templates/launderette/launderette_admin.jinja:44
msgid "User"
msgstr "Utilisateur"
-#: accounting/models.py:338 club/models.py:334
+#: accounting/models.py:336 club/models.py:427
#: club/templates/club/club_detail.jinja:12
#: com/templates/com/mailing_admin.jinja:11
#: com/templates/com/news_admin_list.jinja:23
@@ -200,7 +200,7 @@ msgstr "Utilisateur"
#: com/templates/com/news_admin_list.jinja:288
#: com/templates/com/weekmail.jinja:18 com/templates/com/weekmail.jinja:47
#: core/templates/core/user_clubs.jinja:15
-#: core/templates/core/user_clubs.jinja:44
+#: core/templates/core/user_clubs.jinja:46
#: counter/templates/counter/invoices_call.jinja:23
#: trombi/templates/trombi/edit_profile.jinja:15
#: trombi/templates/trombi/edit_profile.jinja:22
@@ -211,36 +211,36 @@ msgstr "Utilisateur"
msgid "Club"
msgstr "Club"
-#: accounting/models.py:339 core/views/user.py:277
+#: accounting/models.py:337 core/views/user.py:277
msgid "Account"
msgstr "Compte"
-#: accounting/models.py:340
+#: accounting/models.py:338
msgid "Company"
msgstr "Entreprise"
-#: accounting/models.py:341 core/models.py:230 sith/settings.py:393
+#: accounting/models.py:339 core/models.py:308 sith/settings.py:398
#: stock/templates/stock/shopping_list_items.jinja:37
msgid "Other"
msgstr "Autre"
-#: accounting/models.py:344
+#: accounting/models.py:342
msgid "target id"
msgstr "id de la cible"
-#: accounting/models.py:346
+#: accounting/models.py:344
msgid "target label"
msgstr "nom de la cible"
-#: accounting/models.py:351
+#: accounting/models.py:349
msgid "linked operation"
msgstr "opération liée"
-#: accounting/models.py:371
+#: accounting/models.py:369
msgid "The date must be set."
msgstr "La date doit être indiquée."
-#: accounting/models.py:375
+#: accounting/models.py:373
#, python-format
msgid ""
"The date can not be before the start date of the journal, which is\n"
@@ -249,16 +249,16 @@ msgstr ""
"La date ne peut pas être avant la date de début du journal, qui est\n"
"%(start_date)s."
-#: accounting/models.py:385
+#: accounting/models.py:383
msgid "Target does not exists"
msgstr "La cible n'existe pas."
-#: accounting/models.py:388
+#: accounting/models.py:386
msgid "Please add a target label if you set no existing target"
msgstr ""
"Merci d'ajouter un nom de cible si vous ne spécifiez pas de cible existante"
-#: accounting/models.py:393
+#: accounting/models.py:391
msgid ""
"You need to provide ether a simplified accounting type or a standard "
"accounting type"
@@ -266,7 +266,7 @@ msgstr ""
"Vous devez fournir soit un type comptable simplifié ou un type comptable "
"standard"
-#: accounting/models.py:467 counter/models.py:265 pedagogy/models.py:46
+#: accounting/models.py:467 counter/models.py:257 pedagogy/models.py:45
msgid "code"
msgstr "code"
@@ -281,14 +281,14 @@ msgstr "type de mouvement"
#: accounting/models.py:479
#: accounting/templates/accounting/journal_statement_nature.jinja:9
#: accounting/templates/accounting/journal_statement_person.jinja:12
-#: accounting/views.py:602
+#: accounting/views.py:594
msgid "Credit"
msgstr "Crédit"
#: accounting/models.py:480
#: accounting/templates/accounting/journal_statement_nature.jinja:28
#: accounting/templates/accounting/journal_statement_person.jinja:40
-#: accounting/views.py:602
+#: accounting/views.py:594
msgid "Debit"
msgstr "Débit"
@@ -296,11 +296,11 @@ msgstr "Débit"
msgid "Neutral"
msgstr "Neutre"
-#: accounting/models.py:514
+#: accounting/models.py:516
msgid "simplified accounting types"
msgstr "type simplifié"
-#: accounting/models.py:519
+#: accounting/models.py:521
msgid "simplified type"
msgstr "type simplifié"
@@ -317,7 +317,7 @@ msgstr "Liste des types comptable"
#: accounting/templates/accounting/label_list.jinja:10
#: accounting/templates/accounting/operation_edit.jinja:10
#: accounting/templates/accounting/simplifiedaccountingtype_list.jinja:10
-#: core/templates/core/user_tools.jinja:58
+#: core/templates/core/user_tools.jinja:96
msgid "Accounting"
msgstr "Comptabilité"
@@ -336,7 +336,7 @@ msgstr "Il n'y a pas de types comptable dans ce site web."
#: accounting/templates/accounting/bank_account_details.jinja:4
#: accounting/templates/accounting/bank_account_details.jinja:14
-#: core/templates/core/user_tools.jinja:67
+#: core/templates/core/user_tools.jinja:109
msgid "Bank account: "
msgstr "Compte en banque : "
@@ -368,22 +368,23 @@ msgstr "Compte en banque : "
#: core/templates/core/macros.jinja:115 core/templates/core/page_prop.jinja:14
#: core/templates/core/user_account_detail.jinja:38
#: core/templates/core/user_account_detail.jinja:66
-#: core/templates/core/user_clubs.jinja:32
-#: core/templates/core/user_clubs.jinja:61
-#: core/templates/core/user_detail.jinja:186
-#: core/templates/core/user_edit.jinja:19
-#: core/templates/core/user_preferences.jinja:36
+#: core/templates/core/user_clubs.jinja:34
+#: core/templates/core/user_clubs.jinja:63
+#: core/templates/core/user_edit.jinja:39
+#: core/templates/core/user_edit.jinja:58
+#: core/templates/core/user_edit.jinja:77
+#: core/templates/core/user_preferences.jinja:48
#: counter/templates/counter/last_ops.jinja:35
#: counter/templates/counter/last_ops.jinja:65
#: election/templates/election/election_detail.jinja:187
#: forum/templates/forum/macros.jinja:21 forum/templates/forum/macros.jinja:134
#: launderette/templates/launderette/launderette_admin.jinja:16
-#: launderette/views.py:227 pedagogy/templates/pedagogy/guide.jinja:67
+#: launderette/views.py:218 pedagogy/templates/pedagogy/guide.jinja:67
#: pedagogy/templates/pedagogy/guide.jinja:90
#: pedagogy/templates/pedagogy/guide.jinja:126
#: pedagogy/templates/pedagogy/uv_detail.jinja:185
-#: sas/templates/sas/album.jinja:27 sas/templates/sas/moderation.jinja:18
-#: sas/templates/sas/picture.jinja:74 sas/templates/sas/picture.jinja:124
+#: sas/templates/sas/album.jinja:37 sas/templates/sas/main.jinja:63
+#: sas/templates/sas/moderation.jinja:18 sas/templates/sas/picture.jinja:64
#: stock/templates/stock/stock_shopping_list.jinja:43
#: stock/templates/stock/stock_shopping_list.jinja:69
#: trombi/templates/trombi/detail.jinja:35
@@ -392,7 +393,7 @@ msgid "Delete"
msgstr "Supprimer"
#: accounting/templates/accounting/bank_account_details.jinja:18
-#: club/views.py:88 core/views/user.py:197 sas/templates/sas/picture.jinja:86
+#: club/views.py:80 core/views/user.py:196 sas/templates/sas/picture.jinja:79
msgid "Infos"
msgstr "Infos"
@@ -411,7 +412,7 @@ msgstr "Nouveau compte club"
#: accounting/templates/accounting/bank_account_details.jinja:27
#: accounting/templates/accounting/bank_account_list.jinja:22
#: accounting/templates/accounting/club_account_details.jinja:58
-#: accounting/templates/accounting/journal_details.jinja:89 club/views.py:134
+#: accounting/templates/accounting/journal_details.jinja:92 club/views.py:126
#: com/templates/com/news_admin_list.jinja:39
#: com/templates/com/news_admin_list.jinja:68
#: com/templates/com/news_admin_list.jinja:115
@@ -426,7 +427,7 @@ msgstr "Nouveau compte club"
#: com/templates/com/weekmail.jinja:61 core/templates/core/file.jinja:38
#: core/templates/core/group_list.jinja:24 core/templates/core/page.jinja:35
#: core/templates/core/poster_list.jinja:40
-#: core/templates/core/user_tools.jinja:43 core/views/user.py:227
+#: core/templates/core/user_tools.jinja:71 core/views/user.py:226
#: counter/templates/counter/cash_summary_list.jinja:53
#: counter/templates/counter/counter_list.jinja:17
#: counter/templates/counter/counter_list.jinja:33
@@ -439,8 +440,7 @@ msgstr "Nouveau compte club"
#: pedagogy/templates/pedagogy/guide.jinja:89
#: pedagogy/templates/pedagogy/guide.jinja:125
#: pedagogy/templates/pedagogy/uv_detail.jinja:184
-#: sas/templates/sas/album.jinja:19 sas/templates/sas/picture.jinja:100
-#: trombi/templates/trombi/detail.jinja:9
+#: sas/templates/sas/album.jinja:36 trombi/templates/trombi/detail.jinja:9
#: trombi/templates/trombi/edit_profile.jinja:34
msgid "Edit"
msgstr "Éditer"
@@ -530,7 +530,7 @@ msgid "Effective amount"
msgstr "Montant effectif"
#: accounting/templates/accounting/club_account_details.jinja:36
-#: sith/settings.py:439
+#: sith/settings.py:444
msgid "Closed"
msgstr "Fermé"
@@ -575,15 +575,15 @@ msgstr "Voir"
#: accounting/templates/accounting/co_list.jinja:4
#: accounting/templates/accounting/journal_details.jinja:19
-#: core/templates/core/user_tools.jinja:63
+#: core/templates/core/user_tools.jinja:103
msgid "Company list"
msgstr "Liste des entreprises"
-#: accounting/templates/accounting/co_list.jinja:10
+#: accounting/templates/accounting/co_list.jinja:12
msgid "Create new company"
msgstr "Nouvelle entreprise"
-#: accounting/templates/accounting/co_list.jinja:17
+#: accounting/templates/accounting/co_list.jinja:18
msgid "Companies"
msgstr "Entreprises"
@@ -629,7 +629,7 @@ msgstr "No"
#: counter/templates/counter/last_ops.jinja:20
#: counter/templates/counter/last_ops.jinja:45
#: counter/templates/counter/refilling_list.jinja:16
-#: rootplace/templates/rootplace/logs.jinja:12 sas/views.py:375
+#: rootplace/templates/rootplace/logs.jinja:12 sas/views.py:364
#: stock/templates/stock/stock_shopping_list.jinja:25
#: stock/templates/stock/stock_shopping_list.jinja:54
#: trombi/templates/trombi/user_profile.jinja:40
@@ -653,7 +653,7 @@ msgid "Target"
msgstr "Cible"
#: accounting/templates/accounting/journal_details.jinja:38
-#: core/views/forms.py:98
+#: core/views/forms.py:95
msgid "Code"
msgstr "Code"
@@ -667,7 +667,7 @@ msgid "Done"
msgstr "Effectuées"
#: accounting/templates/accounting/journal_details.jinja:41
-#: counter/templates/counter/cash_summary_list.jinja:37 counter/views.py:1084
+#: counter/templates/counter/cash_summary_list.jinja:37 counter/views.py:1078
#: pedagogy/templates/pedagogy/moderation.jinja:13
#: pedagogy/templates/pedagogy/uv_detail.jinja:138
#: trombi/templates/trombi/comment.jinja:4
@@ -701,7 +701,7 @@ msgstr ""
"Ouvrez un classeur dans ce compte club, puis sauver "
"cette opération à nouveau pour créer l'opération liée."
-#: accounting/templates/accounting/journal_details.jinja:93
+#: accounting/templates/accounting/journal_details.jinja:96
msgid "Generate"
msgstr "Générer"
@@ -784,10 +784,10 @@ msgstr "Opération liée : "
#: core/templates/core/file_edit.jinja:8
#: core/templates/core/macros_pages.jinja:25
#: core/templates/core/page_prop.jinja:11
-#: core/templates/core/user_godfathers.jinja:41
-#: core/templates/core/user_preferences.jinja:12
-#: core/templates/core/user_preferences.jinja:19
-#: core/templates/core/user_preferences.jinja:31
+#: core/templates/core/user_godfathers.jinja:62
+#: core/templates/core/user_preferences.jinja:18
+#: core/templates/core/user_preferences.jinja:27
+#: core/templates/core/user_preferences.jinja:65
#: counter/templates/counter/cash_register_summary.jinja:28
#: forum/templates/forum/reply.jinja:33
#: subscription/templates/subscription/subscription.jinja:25
@@ -799,7 +799,7 @@ msgstr "Sauver"
#: accounting/templates/accounting/refound_account.jinja:4
#: accounting/templates/accounting/refound_account.jinja:9
-#: accounting/views.py:933
+#: accounting/views.py:925
msgid "Refound account"
msgstr "Remboursement de compte"
@@ -820,189 +820,189 @@ msgstr "Types simplifiés"
msgid "New simplified type"
msgstr "Nouveau type simplifié"
-#: accounting/views.py:247 accounting/views.py:257 accounting/views.py:577
+#: accounting/views.py:239 accounting/views.py:249 accounting/views.py:569
msgid "Journal"
msgstr "Classeur"
-#: accounting/views.py:267
+#: accounting/views.py:259
msgid "Statement by nature"
msgstr "Bilan par nature"
-#: accounting/views.py:277
+#: accounting/views.py:269
msgid "Statement by person"
msgstr "Bilan par personne"
-#: accounting/views.py:287
+#: accounting/views.py:279
msgid "Accounting statement"
msgstr "Bilan comptable"
-#: accounting/views.py:391
+#: accounting/views.py:383
msgid "Link this operation to the target account"
msgstr "Lier cette opération au compte cible"
-#: accounting/views.py:421
+#: accounting/views.py:413
msgid "The target must be set."
msgstr "La cible doit être indiquée."
-#: accounting/views.py:436
+#: accounting/views.py:428
msgid "The amount must be set."
msgstr "Le montant doit être indiqué."
-#: accounting/views.py:571 accounting/views.py:577
+#: accounting/views.py:563 accounting/views.py:569
msgid "Operation"
msgstr "Opération"
-#: accounting/views.py:586
+#: accounting/views.py:578
msgid "Financial proof: "
msgstr "Justificatif de libellé : "
-#: accounting/views.py:589
+#: accounting/views.py:581
#, python-format
msgid "Club: %(club_name)s"
msgstr "Club : %(club_name)s"
-#: accounting/views.py:594
+#: accounting/views.py:586
#, python-format
msgid "Label: %(op_label)s"
msgstr "Libellé : %(op_label)s"
-#: accounting/views.py:597
+#: accounting/views.py:589
#, python-format
msgid "Date: %(date)s"
msgstr "Date : %(date)s"
-#: accounting/views.py:605
+#: accounting/views.py:597
#, python-format
msgid "Amount: %(amount).2f €"
msgstr "Montant : %(amount).2f €"
-#: accounting/views.py:620
+#: accounting/views.py:612
msgid "Debtor"
msgstr "Débiteur"
-#: accounting/views.py:620
+#: accounting/views.py:612
msgid "Creditor"
msgstr "Créditeur"
-#: accounting/views.py:625
+#: accounting/views.py:617
msgid "Comment:"
msgstr "Commentaire :"
-#: accounting/views.py:650
+#: accounting/views.py:642
msgid "Signature:"
msgstr "Signature :"
-#: accounting/views.py:718
+#: accounting/views.py:710
msgid "General statement"
msgstr "Bilan général"
-#: accounting/views.py:725
+#: accounting/views.py:717
msgid "No label operations"
msgstr "Opérations sans étiquette"
-#: accounting/views.py:889
+#: accounting/views.py:881
msgid "Refound this account"
msgstr "Rembourser ce compte"
-#: club/forms.py:61 club/forms.py:194
+#: club/forms.py:58 club/forms.py:190
msgid "Users to add"
msgstr "Utilisateurs à ajouter"
-#: club/forms.py:62 club/forms.py:195 core/views/group.py:63
+#: club/forms.py:59 club/forms.py:191 core/views/group.py:52
msgid "Search users to add (one or more)."
msgstr "Recherche les utilisateurs à ajouter (un ou plus)."
-#: club/forms.py:71
+#: club/forms.py:68
msgid "New Mailing"
msgstr "Nouvelle mailing liste"
-#: club/forms.py:72
+#: club/forms.py:69
msgid "Subscribe"
msgstr "S'abonner"
-#: club/forms.py:73 club/forms.py:86 com/templates/com/news_admin_list.jinja:40
+#: club/forms.py:70 club/forms.py:83 com/templates/com/news_admin_list.jinja:40
#: com/templates/com/news_admin_list.jinja:116
#: com/templates/com/news_admin_list.jinja:198
#: com/templates/com/news_admin_list.jinja:274
msgid "Remove"
msgstr "Retirer"
-#: club/forms.py:76 launderette/views.py:229
+#: club/forms.py:73 launderette/views.py:220
#: pedagogy/templates/pedagogy/moderation.jinja:15
msgid "Action"
msgstr "Action"
-#: club/forms.py:116 club/tests.py:578
+#: club/forms.py:113 club/tests.py:742
msgid "This field is required"
msgstr "Ce champ est obligatoire"
-#: club/forms.py:128 club/forms.py:256 club/tests.py:590
+#: club/forms.py:125 club/forms.py:250 club/tests.py:755
msgid "One of the selected users doesn't exist"
msgstr "Un des utilisateurs sélectionné n'existe pas"
-#: club/forms.py:132 club/tests.py:608
+#: club/forms.py:129 club/tests.py:772
msgid "One of the selected users doesn't have an email address"
msgstr "Un des utilisateurs sélectionnés n'a pas d'adresse email"
-#: club/forms.py:143
+#: club/forms.py:140
msgid "An action is required"
msgstr "Une action est requise"
-#: club/forms.py:154 club/tests.py:567
+#: club/forms.py:151 club/tests.py:729
msgid "You must specify at least an user or an email address"
msgstr "vous devez spécifier au moins un utilisateur ou une adresse email"
-#: club/forms.py:162 counter/forms.py:165
+#: club/forms.py:159 counter/forms.py:165
msgid "Begin date"
msgstr "Date de début"
-#: club/forms.py:163 com/views.py:84 com/views.py:199 counter/forms.py:166
-#: election/views.py:172 subscription/views.py:49
+#: club/forms.py:160 com/views.py:81 com/views.py:196 counter/forms.py:166
+#: election/views.py:167 subscription/views.py:39
msgid "End date"
msgstr "Date de fin"
-#: club/forms.py:166 club/templates/club/club_sellings.jinja:21
+#: club/forms.py:163 club/templates/club/club_sellings.jinja:21
#: core/templates/core/user_account_detail.jinja:18
#: core/templates/core/user_account_detail.jinja:51
-#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:158
+#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:149
msgid "Counter"
msgstr "Comptoir"
-#: club/forms.py:174 counter/views.py:782
+#: club/forms.py:170 counter/views.py:776
msgid "Products"
msgstr "Produits"
-#: club/forms.py:179 counter/views.py:787
+#: club/forms.py:175 counter/views.py:781
msgid "Archived products"
msgstr "Produits archivés"
-#: club/forms.py:238 club/templates/club/club_members.jinja:21
-#: club/templates/club/club_members.jinja:46
-#: core/templates/core/user_clubs.jinja:29
+#: club/forms.py:232 club/templates/club/club_members.jinja:22
+#: club/templates/club/club_members.jinja:48
+#: core/templates/core/user_clubs.jinja:31
msgid "Mark as old"
msgstr "Marquer comme ancien"
-#: club/forms.py:260
+#: club/forms.py:254
msgid "User must be subscriber to take part to a club"
msgstr "L'utilisateur doit être cotisant pour faire partie d'un club"
-#: club/forms.py:264 core/views/group.py:82
+#: club/forms.py:258 core/views/group.py:71
msgid "You can not add the same user twice"
msgstr "Vous ne pouvez pas ajouter deux fois le même utilisateur"
-#: club/forms.py:285
+#: club/forms.py:279
msgid "You should specify a role"
msgstr "Vous devez choisir un rôle"
-#: club/forms.py:296 sas/views.py:130 sas/views.py:202 sas/views.py:301
+#: club/forms.py:290 sas/views.py:119 sas/views.py:191 sas/views.py:290
msgid "You do not have the permission to do that"
msgstr "Vous n'avez pas la permission de faire cela"
-#: club/models.py:53
+#: club/models.py:55
msgid "unix name"
msgstr "nom unix"
-#: club/models.py:60
+#: club/models.py:62
msgid ""
"Enter a valid unix name. This value may contain only letters, numbers ./-/_ "
"characters."
@@ -1010,94 +1010,94 @@ msgstr ""
"Entrez un nom UNIX valide. Cette valeur peut contenir uniquement des "
"lettres, des nombres, et les caractères ./-/_"
-#: club/models.py:65
+#: club/models.py:67
msgid "A club with that unix name already exists."
msgstr "Un club avec ce nom UNIX existe déjà."
-#: club/models.py:68
+#: club/models.py:70
msgid "logo"
msgstr "logo"
-#: club/models.py:70
+#: club/models.py:72
msgid "is active"
msgstr "actif"
-#: club/models.py:72
+#: club/models.py:74
msgid "short description"
msgstr "description courte"
-#: club/models.py:74 core/models.py:285
+#: club/models.py:76 core/models.py:363
msgid "address"
msgstr "Adresse"
-#: club/models.py:94 core/models.py:196
+#: club/models.py:97 core/models.py:274
msgid "home"
msgstr "home"
-#: club/models.py:118
+#: club/models.py:121
msgid "You can not make loops in clubs"
msgstr "Vous ne pouvez pas faire de boucles dans les clubs"
-#: club/models.py:132
+#: club/models.py:145
msgid "A club with that unix_name already exists"
msgstr "Un club avec ce nom UNIX existe déjà."
-#: club/models.py:267 counter/models.py:869 counter/models.py:905
-#: eboutic/models.py:63 eboutic/models.py:232 election/models.py:192
-#: launderette/models.py:145 launderette/models.py:213 sas/models.py:244
+#: club/models.py:348 counter/models.py:874 counter/models.py:910
+#: eboutic/models.py:54 eboutic/models.py:223 election/models.py:191
+#: launderette/models.py:141 launderette/models.py:211 sas/models.py:240
#: trombi/models.py:213
msgid "user"
msgstr "nom d'utilisateur"
-#: club/models.py:284 core/models.py:249 election/models.py:187
-#: election/models.py:223 trombi/models.py:218
+#: club/models.py:365 core/models.py:327 election/models.py:186
+#: election/models.py:222 trombi/models.py:218
msgid "role"
msgstr "rôle"
-#: club/models.py:289 core/models.py:81 counter/models.py:223
-#: counter/models.py:256 election/models.py:15 election/models.py:120
-#: election/models.py:197 forum/models.py:59 forum/models.py:240
+#: club/models.py:370 core/models.py:85 counter/models.py:213
+#: counter/models.py:248 election/models.py:15 election/models.py:119
+#: election/models.py:196 forum/models.py:59 forum/models.py:243
msgid "description"
msgstr "description"
-#: club/models.py:299
+#: club/models.py:382
msgid "past member"
msgstr "Anciens membres"
-#: club/models.py:341 club/models.py:444
+#: club/models.py:434 club/models.py:534
msgid "Email address"
msgstr "Adresse email"
-#: club/models.py:349
+#: club/models.py:442
msgid "Enter a valid address. Only the root of the address is needed."
msgstr ""
"Entrez une adresse valide. Seule la racine de l'adresse est nécessaire."
-#: club/models.py:353 com/models.py:83 com/models.py:312 core/models.py:863
+#: club/models.py:446 com/models.py:85 com/models.py:322 core/models.py:915
msgid "is moderated"
msgstr "est modéré"
-#: club/models.py:357 com/models.py:87 com/models.py:316
+#: club/models.py:450 com/models.py:89 com/models.py:326
msgid "moderator"
msgstr "modérateur"
-#: club/models.py:364
+#: club/models.py:457
msgid "This mailing list already exists."
msgstr "Cette liste de diffusion existe déjà."
-#: club/models.py:430 club/templates/club/mailing.jinja:23
+#: club/models.py:520 club/templates/club/mailing.jinja:23
msgid "Mailing"
msgstr "Liste de diffusion"
-#: club/models.py:451
+#: club/models.py:541
msgid "At least user or email is required"
msgstr "Au moins un utilisateur ou un email est nécessaire"
-#: club/models.py:459 club/tests.py:636
+#: club/models.py:549 club/tests.py:800
msgid "This email is already suscribed in this mailing"
msgstr "Cet email est déjà abonné à cette mailing"
-#: club/models.py:485
+#: club/models.py:577
msgid "Unregistered user"
msgstr "Utilisateur non enregistré"
@@ -1110,7 +1110,7 @@ msgid "inactive"
msgstr "inactif"
#: club/templates/club/club_list.jinja:34
-#: core/templates/core/user_tools.jinja:24
+#: core/templates/core/user_tools.jinja:31
msgid "New club"
msgstr "Nouveau club"
@@ -1122,37 +1122,37 @@ msgstr "Il n'y a pas de club dans ce site web."
msgid "Club members"
msgstr "Membres du club"
-#: club/templates/club/club_members.jinja:17
+#: club/templates/club/club_members.jinja:18
#: club/templates/club/club_old_members.jinja:9
#: core/templates/core/user_clubs.jinja:16
-#: core/templates/core/user_clubs.jinja:45
+#: core/templates/core/user_clubs.jinja:47
#: trombi/templates/trombi/edit_profile.jinja:23
#: trombi/templates/trombi/export.jinja:56
#: trombi/templates/trombi/user_profile.jinja:39
msgid "Role"
msgstr "Rôle"
-#: club/templates/club/club_members.jinja:18
+#: club/templates/club/club_members.jinja:19
#: club/templates/club/club_old_members.jinja:10
#: core/templates/core/group_list.jinja:15
#: core/templates/core/user_clubs.jinja:17
-#: core/templates/core/user_clubs.jinja:46
+#: core/templates/core/user_clubs.jinja:48
msgid "Description"
msgstr "Description"
-#: club/templates/club/club_members.jinja:19
+#: club/templates/club/club_members.jinja:20
#: core/templates/core/user_clubs.jinja:18
#: launderette/templates/launderette/launderette_admin.jinja:45
msgid "Since"
msgstr "Depuis"
-#: club/templates/club/club_members.jinja:50
+#: club/templates/club/club_members.jinja:52
msgid "There are no members in this club."
msgstr "Il n'y a pas de membres dans ce club."
-#: club/templates/club/club_members.jinja:78
-#: core/templates/core/file_detail.jinja:19 core/views/forms.py:345
-#: launderette/views.py:227 trombi/templates/trombi/detail.jinja:19
+#: club/templates/club/club_members.jinja:80
+#: core/templates/core/file_detail.jinja:19 core/views/forms.py:343
+#: launderette/views.py:218 trombi/templates/trombi/detail.jinja:19
msgid "Add"
msgstr "Ajouter"
@@ -1161,17 +1161,17 @@ msgid "Club old members"
msgstr "Anciens membres du club"
#: club/templates/club/club_old_members.jinja:11
-#: core/templates/core/user_clubs.jinja:47
+#: core/templates/core/user_clubs.jinja:49
msgid "From"
msgstr "Du"
#: club/templates/club/club_old_members.jinja:12
-#: core/templates/core/user_clubs.jinja:48
+#: core/templates/core/user_clubs.jinja:50
msgid "To"
msgstr "Au"
-#: club/templates/club/club_sellings.jinja:5 club/views.py:154
-#: club/views.py:483 counter/templates/counter/counter_main.jinja:24
+#: club/templates/club/club_sellings.jinja:5
+#: counter/templates/counter/counter_main.jinja:24
#: counter/templates/counter/last_ops.jinja:41
msgid "Sales"
msgstr "Ventes"
@@ -1215,7 +1215,7 @@ msgstr "Client"
#: club/templates/club/club_sellings.jinja:25
#: core/templates/core/user_account_detail.jinja:21
-#: core/templates/core/user_stats.jinja:28
+#: core/templates/core/user_stats.jinja:44
#: counter/templates/counter/last_ops.jinja:49
msgid "Quantity"
msgstr "Quantité"
@@ -1225,7 +1225,7 @@ msgstr "Quantité"
#: core/templates/core/user_account_detail.jinja:22
#: counter/templates/counter/cash_summary_list.jinja:35
#: counter/templates/counter/last_ops.jinja:50
-#: counter/templates/counter/stats.jinja:21
+#: counter/templates/counter/stats.jinja:23
#: subscription/templates/subscription/stats.jinja:40
#: subscription/templates/subscription/stats.jinja:48
msgid "Total"
@@ -1234,7 +1234,7 @@ msgstr "Total"
#: club/templates/club/club_sellings.jinja:27
#: core/templates/core/user_account_detail.jinja:23
#: core/templates/core/user_account_detail.jinja:54
-#: core/templates/core/user_detail.jinja:156
+#: core/templates/core/user_detail.jinja:190
#: counter/templates/counter/last_ops.jinja:24
#: counter/templates/counter/last_ops.jinja:51
#: counter/templates/counter/refilling_list.jinja:14
@@ -1242,7 +1242,7 @@ msgid "Payment method"
msgstr "Méthode de paiement"
#: club/templates/club/club_tools.jinja:4
-#: core/templates/core/user_tools.jinja:101
+#: core/templates/core/user_tools.jinja:157
msgid "Club tools"
msgstr "Outils club"
@@ -1269,7 +1269,7 @@ msgstr "Nouveau Trombi"
#: club/templates/club/club_tools.jinja:14
#: com/templates/com/poster_list.jinja:17
#: core/templates/core/poster_list.jinja:17
-#: core/templates/core/user_tools.jinja:91
+#: core/templates/core/user_tools.jinja:145
msgid "Posters"
msgstr "Affiches"
@@ -1347,148 +1347,154 @@ msgstr "Aucune page n'existe pour ce club"
msgid "Club stats"
msgstr "Statistiques du club"
-#: club/views.py:98
+#: club/views.py:90
msgid "Members"
msgstr "Membres"
-#: club/views.py:107
+#: club/views.py:99
msgid "Old members"
msgstr "Anciens membres"
-#: club/views.py:117 core/templates/core/page.jinja:33
+#: club/views.py:109 core/templates/core/page.jinja:33
msgid "History"
msgstr "Historique"
-#: club/views.py:125 core/templates/core/base.jinja:129 core/views/user.py:220
-#: sas/templates/sas/picture.jinja:95 trombi/views.py:63
+#: club/views.py:117 core/templates/core/base.jinja:95 core/views/user.py:219
+#: sas/templates/sas/picture.jinja:100 trombi/views.py:62
msgid "Tools"
msgstr "Outils"
-#: club/views.py:145
+#: club/views.py:137
msgid "Edit club page"
msgstr "Éditer la page de club"
-#: club/views.py:161
+#: club/views.py:146 club/views.py:472
+#, fuzzy
+#| msgid "Selling"
+msgid "Sellings"
+msgstr "Vente"
+
+#: club/views.py:153
msgid "Mailing list"
msgstr "Listes de diffusion"
-#: club/views.py:170 com/views.py:134
+#: club/views.py:162 com/views.py:131
msgid "Posters list"
msgstr "Liste d'affiches"
-#: club/views.py:180 counter/templates/counter/counter_list.jinja:21
+#: club/views.py:172 counter/templates/counter/counter_list.jinja:21
#: counter/templates/counter/counter_list.jinja:43
#: counter/templates/counter/counter_list.jinja:59
msgid "Props"
msgstr "Propriétés"
-#: com/models.py:46
+#: com/models.py:45
msgid "alert message"
msgstr "message d'alerte"
-#: com/models.py:47
+#: com/models.py:46
msgid "info message"
msgstr "message d'info"
-#: com/models.py:48
+#: com/models.py:47
msgid "weekmail destinations"
msgstr "destinataires du weekmail"
-#: com/models.py:58
+#: com/models.py:60
msgid "Notice"
msgstr "Information"
-#: com/models.py:59
+#: com/models.py:61
msgid "Event"
msgstr "Événement"
-#: com/models.py:60
+#: com/models.py:62
msgid "Weekly"
msgstr "Hebdomadaire"
-#: com/models.py:61
+#: com/models.py:63
msgid "Call"
msgstr "Appel"
-#: com/models.py:68 com/models.py:175 com/models.py:255 election/models.py:14
-#: election/models.py:119 election/models.py:159 forum/models.py:251
-#: forum/models.py:309 pedagogy/models.py:101
+#: com/models.py:70 com/models.py:179 com/models.py:261 election/models.py:14
+#: election/models.py:118 election/models.py:158 forum/models.py:254
+#: forum/models.py:312 pedagogy/models.py:100
msgid "title"
msgstr "titre"
-#: com/models.py:69
+#: com/models.py:71
msgid "summary"
msgstr "résumé"
-#: com/models.py:70 com/models.py:256 trombi/models.py:197
+#: com/models.py:72 com/models.py:262 trombi/models.py:197
msgid "content"
msgstr "contenu"
-#: com/models.py:72 core/models.py:1446 launderette/models.py:101
-#: launderette/models.py:139 launderette/models.py:196 stock/models.py:80
-#: stock/models.py:137
+#: com/models.py:74 core/models.py:1523 launderette/models.py:95
+#: launderette/models.py:135 launderette/models.py:194 stock/models.py:79
+#: stock/models.py:136
msgid "type"
msgstr "type"
-#: com/models.py:80 com/models.py:260 pedagogy/models.py:61
-#: pedagogy/models.py:211 trombi/models.py:187
+#: com/models.py:82 com/models.py:266 pedagogy/models.py:60
+#: pedagogy/models.py:210 trombi/models.py:187
msgid "author"
msgstr "auteur"
-#: com/models.py:153
+#: com/models.py:157
msgid "news_date"
msgstr "date de la nouvelle"
-#: com/models.py:156
+#: com/models.py:160
msgid "start_date"
msgstr "date de début"
-#: com/models.py:157
+#: com/models.py:161
msgid "end_date"
msgstr "date de fin"
-#: com/models.py:176
+#: com/models.py:180
msgid "intro"
msgstr "intro"
-#: com/models.py:177
+#: com/models.py:181
msgid "joke"
msgstr "blague"
-#: com/models.py:178
+#: com/models.py:182
msgid "protip"
msgstr "astuce"
-#: com/models.py:179
+#: com/models.py:183
msgid "conclusion"
msgstr "conclusion"
-#: com/models.py:180
+#: com/models.py:184
msgid "sent"
msgstr "envoyé"
-#: com/models.py:251
+#: com/models.py:257
msgid "weekmail"
msgstr "weekmail"
-#: com/models.py:269
+#: com/models.py:275
msgid "rank"
msgstr "rang"
-#: com/models.py:298 core/models.py:828 core/models.py:878
+#: com/models.py:308 core/models.py:880 core/models.py:930
msgid "file"
msgstr "fichier"
-#: com/models.py:310
+#: com/models.py:320
msgid "display time"
msgstr "temps d'affichage"
-#: com/models.py:338
+#: com/models.py:348
msgid "Begin date should be before end date"
msgstr "La date de début doit être avant celle de fin"
-#: com/templates/com/mailing_admin.jinja:4 com/views.py:127
-#: core/templates/core/user_tools.jinja:90
+#: com/templates/com/mailing_admin.jinja:4 com/views.py:124
+#: core/templates/core/user_tools.jinja:144
msgid "Mailing lists administration"
msgstr "Administration des mailing listes"
@@ -1500,7 +1506,7 @@ msgstr "Administration des mailing listes"
#: com/templates/com/news_detail.jinja:39
#: core/templates/core/file_detail.jinja:65
#: core/templates/core/file_moderation.jinja:23
-#: sas/templates/sas/moderation.jinja:17 sas/templates/sas/picture.jinja:122
+#: sas/templates/sas/moderation.jinja:17 sas/templates/sas/picture.jinja:61
msgid "Moderate"
msgstr "Modérer"
@@ -1536,7 +1542,7 @@ msgstr "Nouvelles"
#: com/templates/com/news_admin_list.jinja:11
#: com/templates/com/news_edit.jinja:8 com/templates/com/news_edit.jinja:31
-#: core/templates/core/user_tools.jinja:85
+#: core/templates/core/user_tools.jinja:139
msgid "Create news"
msgstr "Créer nouvelle"
@@ -1557,7 +1563,7 @@ msgstr "Informations affichées"
#: com/templates/com/news_admin_list.jinja:248
#: com/templates/com/news_admin_list.jinja:285
#: launderette/templates/launderette/launderette_admin.jinja:42
-#: launderette/views.py:234
+#: launderette/views.py:225
msgid "Type"
msgstr "Type"
@@ -1570,8 +1576,8 @@ msgstr "Type"
#: com/templates/com/news_admin_list.jinja:249
#: com/templates/com/news_admin_list.jinja:286
#: com/templates/com/weekmail.jinja:19 com/templates/com/weekmail.jinja:48
-#: forum/templates/forum/forum.jinja:24 forum/templates/forum/forum.jinja:43
-#: forum/templates/forum/main.jinja:27 forum/views.py:244
+#: forum/templates/forum/forum.jinja:28 forum/templates/forum/forum.jinja:47
+#: forum/templates/forum/main.jinja:30 forum/views.py:243
#: pedagogy/templates/pedagogy/guide.jinja:60
msgid "Title"
msgstr "Titre"
@@ -1596,7 +1602,7 @@ msgstr "Résumé"
#: com/templates/com/news_admin_list.jinja:252
#: com/templates/com/news_admin_list.jinja:289
#: com/templates/com/weekmail.jinja:17 com/templates/com/weekmail.jinja:46
-#: forum/templates/forum/forum.jinja:47
+#: forum/templates/forum/forum.jinja:51
msgid "Author"
msgstr "Auteur"
@@ -1641,12 +1647,8 @@ msgstr "Appels affichés"
msgid "Calls to moderate"
msgstr "Appels à modérer"
-#: core/templates/core/base.jinja
-msgid "Site version:"
-msgstr "Version du site :"
-
#: com/templates/com/news_admin_list.jinja:242
-#: core/templates/core/base.jinja:183
+#: core/templates/core/base.jinja:210
msgid "Events"
msgstr "Événements"
@@ -1666,7 +1668,7 @@ msgstr "Retour aux nouvelles"
msgid "Author: "
msgstr "Auteur : "
-#: com/templates/com/news_detail.jinja:37 sas/templates/sas/picture.jinja:90
+#: com/templates/com/news_detail.jinja:37 sas/templates/sas/picture.jinja:92
msgid "Moderator: "
msgstr "Modérateur : "
@@ -1713,10 +1715,6 @@ msgstr "Administrer les news"
msgid "Events today and the next few days"
msgstr "Événements aujourd'hui et dans les prochains jours"
-#: com/templates/com/news_list.jinja:100
-msgid "All coming events"
-msgstr "Tous les événements à venir"
-
#: com/templates/com/news_list.jinja:82
msgid "Nothing to come..."
msgstr "Rien à venir..."
@@ -1725,20 +1723,24 @@ msgstr "Rien à venir..."
msgid "Coming soon... don't miss!"
msgstr "Prochainement... à ne pas rater!"
-#: com/templates/com/news_list.jinja:104
+#: com/templates/com/news_list.jinja:101
+msgid "All coming events"
+msgstr "Tous les événements à venir"
+
+#: com/templates/com/news_list.jinja:113
msgid "Agenda"
msgstr "Agenda"
-#: com/templates/com/news_list.jinja:128
+#: com/templates/com/news_list.jinja:137
msgid "Birthdays"
msgstr "Anniversaires"
-#: com/templates/com/news_list.jinja:136
+#: com/templates/com/news_list.jinja:145
#, python-format
msgid "%(age)s year old"
msgstr "%(age)s ans"
-#: com/templates/com/news_list.jinja:147 com/tests.py:112 com/tests.py:122
+#: com/templates/com/news_list.jinja:156 com/tests.py:102 com/tests.py:112
msgid "You need an up to date subscription to access this content"
msgstr "Votre cotisation doit être à jour pour accéder à cette section"
@@ -1761,7 +1763,7 @@ msgstr "Affiche - modifier"
#: com/templates/com/poster_list.jinja:20
#: com/templates/com/poster_list.jinja:23
#: com/templates/com/screen_list.jinja:13
-#: core/templates/core/poster_list.jinja:19 sas/templates/sas/main.jinja:53
+#: core/templates/core/poster_list.jinja:19 sas/templates/sas/main.jinja:101
msgid "Create"
msgstr "Créer"
@@ -1792,7 +1794,7 @@ msgid "Screen - edit"
msgstr "Écran - modifier"
#: com/templates/com/screen_list.jinja:4 com/templates/com/screen_list.jinja:11
-#: core/templates/core/user_tools.jinja:92
+#: core/templates/core/user_tools.jinja:146
msgid "Screens"
msgstr "Écrans"
@@ -1807,7 +1809,7 @@ msgid "Slideshow"
msgstr "Diaporama"
#: com/templates/com/weekmail.jinja:5 com/templates/com/weekmail.jinja:9
-#: com/views.py:104 core/templates/core/user_tools.jinja:83
+#: com/views.py:101 core/templates/core/user_tools.jinja:137
msgid "Weekmail"
msgstr "Weekmail"
@@ -1850,7 +1852,7 @@ msgstr "Supprimer du Weekmail"
#: com/templates/com/weekmail_preview.jinja:9
#: core/templates/core/user_account_detail.jinja:11
-#: core/templates/core/user_account_detail.jinja:104 launderette/views.py:227
+#: core/templates/core/user_account_detail.jinja:104 launderette/views.py:218
#: pedagogy/templates/pedagogy/uv_detail.jinja:12
#: pedagogy/templates/pedagogy/uv_detail.jinja:21
#: stock/templates/stock/shopping_list_items.jinja:9
@@ -1904,90 +1906,90 @@ msgstr "Astuce"
msgid "Final word"
msgstr "Le mot de la fin"
-#: com/views.py:77
+#: com/views.py:74
msgid "Format: 16:9 | Resolution: 1920x1080"
msgstr "Format : 16:9 | Résolution : 1920x1080"
-#: com/views.py:80 com/views.py:198 election/views.py:171
-#: subscription/views.py:46
+#: com/views.py:77 com/views.py:195 election/views.py:166
+#: subscription/views.py:36
msgid "Start date"
msgstr "Date de début"
-#: com/views.py:99
+#: com/views.py:96
msgid "Communication administration"
msgstr "Administration de la communication"
-#: com/views.py:110 core/templates/core/user_tools.jinja:84
+#: com/views.py:107 core/templates/core/user_tools.jinja:138
msgid "Weekmail destinations"
msgstr "Destinataires du Weekmail"
-#: com/views.py:114
+#: com/views.py:111
msgid "Info message"
msgstr "Message d'info"
-#: com/views.py:120
+#: com/views.py:117
msgid "Alert message"
msgstr "Message d'alerte"
-#: com/views.py:141
+#: com/views.py:138
msgid "Screens list"
msgstr "Liste d'écrans"
-#: com/views.py:200
+#: com/views.py:197
msgid "Until"
msgstr "Jusqu'à"
-#: com/views.py:202
+#: com/views.py:199
msgid "Automoderation"
msgstr "Automodération"
-#: com/views.py:209 com/views.py:213 com/views.py:227
+#: com/views.py:206 com/views.py:210 com/views.py:224
msgid "This field is required."
msgstr "Ce champ est obligatoire."
-#: com/views.py:223
+#: com/views.py:220
msgid "You crazy? You can not finish an event before starting it."
msgstr "T'es fou? Un événement ne peut pas finir avant même de commencer."
-#: com/views.py:466
+#: com/views.py:459
msgid "Delete and save to regenerate"
msgstr "Supprimer et sauver pour régénérer"
-#: com/views.py:481
+#: com/views.py:474
msgid "Weekmail of the "
msgstr "Weekmail du "
-#: com/views.py:591
+#: com/views.py:584
msgid ""
"You must be a board member of the selected club to post in the Weekmail."
msgstr ""
"Vous devez êtres un membre du bureau du club sélectionné pour poster dans le "
"Weekmail."
-#: core/models.py:76
+#: core/models.py:80
msgid "meta group status"
msgstr "status du meta-groupe"
-#: core/models.py:78
+#: core/models.py:82
msgid "Whether a group is a meta group or not"
msgstr "Si un groupe est un meta-groupe ou pas"
-#: core/models.py:131
+#: core/models.py:171
#, python-format
msgid "%(value)s is not a valid promo (between 0 and %(end)s)"
msgstr "%(value)s n'est pas une promo valide (doit être entre 0 et %(end)s)"
-#: core/models.py:149
+#: core/models.py:227
msgid "username"
msgstr "nom d'utilisateur"
-#: core/models.py:153
+#: core/models.py:231
msgid "Required. 254 characters or fewer. Letters, digits and ./+/-/_ only."
msgstr ""
"Requis. Pas plus de 254 caractères. Uniquement des lettres, numéros, et ./"
"+/-/_"
-#: core/models.py:159
+#: core/models.py:237
msgid ""
"Enter a valid username. This value may contain only letters, numbers and ./"
"+/-/_ characters."
@@ -1995,43 +1997,43 @@ msgstr ""
"Entrez un nom d'utilisateur correct. Uniquement des lettres, numéros, et ./"
"+/-/_"
-#: core/models.py:165
+#: core/models.py:243
msgid "A user with that username already exists."
msgstr "Un utilisateur de ce nom existe déjà"
-#: core/models.py:167
+#: core/models.py:245
msgid "first name"
msgstr "Prénom"
-#: core/models.py:168
+#: core/models.py:246
msgid "last name"
msgstr "Nom"
-#: core/models.py:169
+#: core/models.py:247
msgid "email address"
msgstr "adresse email"
-#: core/models.py:170
+#: core/models.py:248
msgid "date of birth"
msgstr "date de naissance"
-#: core/models.py:171
+#: core/models.py:249
msgid "nick name"
msgstr "surnom"
-#: core/models.py:173
+#: core/models.py:251
msgid "staff status"
msgstr "status \"staff\""
-#: core/models.py:175
+#: core/models.py:253
msgid "Designates whether the user can log into this admin site."
msgstr "Est-ce que l'utilisateur peut se logger à la partie admin du site."
-#: core/models.py:178
+#: core/models.py:256
msgid "active"
msgstr "actif"
-#: core/models.py:181
+#: core/models.py:259
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
@@ -2039,173 +2041,183 @@ msgstr ""
"Est-ce que l'utilisateur doit être traité comme actif. Désélectionnez au "
"lieu de supprimer les comptes."
-#: core/models.py:185
+#: core/models.py:263
msgid "date joined"
msgstr "date d'inscription"
-#: core/models.py:186
+#: core/models.py:264
msgid "last update"
msgstr "dernière mise à jour"
-#: core/models.py:188
+#: core/models.py:266
msgid "superuser"
msgstr "super-utilisateur"
-#: core/models.py:190
+#: core/models.py:268
msgid "Designates whether this user is a superuser. "
msgstr "Est-ce que l'utilisateur est super-utilisateur."
-#: core/models.py:204
+#: core/models.py:282
msgid "profile"
msgstr "profil"
-#: core/models.py:212
+#: core/models.py:290
msgid "avatar"
msgstr "avatar"
-#: core/models.py:220
+#: core/models.py:298
msgid "scrub"
msgstr "blouse"
-#: core/models.py:226
+#: core/models.py:304
msgid "sex"
msgstr "Genre"
-#: core/models.py:230
+#: core/models.py:308
msgid "Man"
msgstr "Homme"
-#: core/models.py:230
+#: core/models.py:308
msgid "Woman"
msgstr "Femme"
-#: core/models.py:232
+#: core/models.py:310
msgid "pronouns"
msgstr "pronoms"
-#: core/models.py:234
+#: core/models.py:312
msgid "tshirt size"
msgstr "taille de t-shirt"
-#: core/models.py:237
+#: core/models.py:315
msgid "-"
msgstr "-"
-#: core/models.py:238
+#: core/models.py:316
msgid "XS"
msgstr "XS"
-#: core/models.py:239
+#: core/models.py:317
msgid "S"
msgstr "S"
-#: core/models.py:240
+#: core/models.py:318
msgid "M"
msgstr "M"
-#: core/models.py:241
+#: core/models.py:319
msgid "L"
msgstr "L"
-#: core/models.py:242
+#: core/models.py:320
msgid "XL"
msgstr "XL"
-#: core/models.py:243
+#: core/models.py:321
msgid "XXL"
msgstr "XXL"
-#: core/models.py:244
+#: core/models.py:322
msgid "XXXL"
msgstr "XXXL"
-#: core/models.py:252
+#: core/models.py:330
msgid "Student"
msgstr "Étudiant"
-#: core/models.py:253
+#: core/models.py:331
msgid "Administrative agent"
msgstr "Personnel administratif"
-#: core/models.py:254
+#: core/models.py:332
msgid "Teacher"
msgstr "Enseignant"
-#: core/models.py:255
+#: core/models.py:333
msgid "Agent"
msgstr "Personnel"
-#: core/models.py:256
+#: core/models.py:334
msgid "Doctor"
msgstr "Doctorant"
-#: core/models.py:257
+#: core/models.py:335
msgid "Former student"
msgstr "Ancien étudiant"
-#: core/models.py:258
+#: core/models.py:336
msgid "Service"
msgstr "Service"
-#: core/models.py:264
+#: core/models.py:342
msgid "department"
msgstr "département"
-#: core/models.py:271
+#: core/models.py:349
msgid "dpt option"
msgstr "Filière"
-#: core/models.py:273 pedagogy/models.py:74 pedagogy/models.py:303
+#: core/models.py:351 pedagogy/models.py:73 pedagogy/models.py:302
msgid "semester"
msgstr "semestre"
-#: core/models.py:274
+#: core/models.py:352
msgid "quote"
msgstr "citation"
-#: core/models.py:275
+#: core/models.py:353
msgid "school"
msgstr "école"
-#: core/models.py:277
+#: core/models.py:355
msgid "promo"
msgstr "promo"
-#: core/models.py:280
+#: core/models.py:358
msgid "forum signature"
msgstr "signature du forum"
-#: core/models.py:282
+#: core/models.py:360
msgid "second email address"
msgstr "adresse email secondaire"
-#: core/models.py:284
+#: core/models.py:362
msgid "parent phone"
msgstr "téléphone des parents"
-#: core/models.py:287
+#: core/models.py:365
msgid "parent address"
msgstr "adresse des parents"
-#: core/models.py:290
+#: core/models.py:368
msgid "is subscriber viewable"
msgstr "profil visible par les cotisants"
-#: core/models.py:513
+#: core/models.py:569
msgid "A user with that username already exists"
msgstr "Un utilisateur de ce nom d'utilisateur existe déjà"
-#: core/models.py:651 core/templates/core/macros.jinja:75
+#: core/models.py:709 core/templates/core/macros.jinja:75
#: core/templates/core/macros.jinja:77 core/templates/core/macros.jinja:78
-#: core/templates/core/user_detail.jinja:87
-#: core/templates/core/user_detail.jinja:88
-#: core/templates/core/user_detail.jinja:90
-#: core/templates/core/user_detail.jinja:91
-#: core/templates/core/user_detail.jinja:96
-#: core/templates/core/user_detail.jinja:97
-#: core/templates/core/user_detail.jinja:99
-#: core/templates/core/user_detail.jinja:100
-#: core/templates/core/user_edit.jinja:17
+#: core/templates/core/user_detail.jinja:104
+#: core/templates/core/user_detail.jinja:105
+#: core/templates/core/user_detail.jinja:107
+#: core/templates/core/user_detail.jinja:108
+#: core/templates/core/user_detail.jinja:113
+#: core/templates/core/user_detail.jinja:114
+#: core/templates/core/user_detail.jinja:116
+#: core/templates/core/user_detail.jinja:117
+#: core/templates/core/user_edit.jinja:24
+#: core/templates/core/user_edit.jinja:26
+#: core/templates/core/user_edit.jinja:27
+#: core/templates/core/user_edit.jinja:46
+#: core/templates/core/user_edit.jinja:47
+#: core/templates/core/user_edit.jinja:49
+#: core/templates/core/user_edit.jinja:50
+#: core/templates/core/user_edit.jinja:65
+#: core/templates/core/user_edit.jinja:66
+#: core/templates/core/user_edit.jinja:68
+#: core/templates/core/user_edit.jinja:69
#: election/templates/election/election_detail.jinja:132
#: election/templates/election/election_detail.jinja:134
#: forum/templates/forum/macros.jinja:104
@@ -2214,109 +2226,101 @@ msgstr "Un utilisateur de ce nom d'utilisateur existe déjà"
msgid "Profile"
msgstr "Profil"
-#: core/models.py:779
+#: core/models.py:833
msgid "Visitor"
msgstr "Visiteur"
-#: core/models.py:787
+#: core/models.py:840
msgid "receive the Weekmail"
msgstr "recevoir le Weekmail"
-#: core/models.py:789
+#: core/models.py:841
msgid "show your stats to others"
msgstr "montrez vos statistiques aux autres"
-#: core/models.py:791
+#: core/models.py:843
msgid "get a notification for every click"
msgstr "avoir une notification pour chaque click"
-#: core/models.py:794
+#: core/models.py:846
msgid "get a notification for every refilling"
msgstr "avoir une notification pour chaque rechargement"
-#: core/models.py:817
+#: core/models.py:869
msgid "file name"
msgstr "nom du fichier"
-#: core/models.py:821 core/models.py:1169
+#: core/models.py:873 core/models.py:1245
msgid "parent"
msgstr "parent"
-#: core/models.py:835
+#: core/models.py:887
msgid "compressed file"
msgstr "version allégée"
-#: core/models.py:842
+#: core/models.py:894
msgid "thumbnail"
msgstr "miniature"
-#: core/models.py:850 core/models.py:867
+#: core/models.py:902 core/models.py:919
msgid "owner"
msgstr "propriétaire"
-#: core/models.py:854 core/models.py:1189 core/views/files.py:193
+#: core/models.py:906 core/models.py:1266 core/views/files.py:224
msgid "edit group"
msgstr "groupe d'édition"
-#: core/models.py:857 core/models.py:1192 core/views/files.py:196
+#: core/models.py:909 core/models.py:1269 core/views/files.py:227
msgid "view group"
msgstr "groupe de vue"
-#: core/models.py:859
+#: core/models.py:911
msgid "is folder"
msgstr "est un dossier"
-#: core/models.py:860
+#: core/models.py:912
msgid "mime type"
msgstr "type mime"
-#: core/models.py:861
+#: core/models.py:913
msgid "size"
msgstr "taille"
-#: core/models.py:872
+#: core/models.py:924
msgid "asked for removal"
msgstr "retrait demandé"
-#: core/models.py:874
+#: core/models.py:926
msgid "is in the SAS"
msgstr "est dans le SAS"
-#: core/models.py:916
+#: core/models.py:998
msgid "Character '/' not authorized in name"
msgstr "Le caractère '/' n'est pas autorisé dans les noms de fichier"
-#: core/models.py:918 core/models.py:922
+#: core/models.py:1000 core/models.py:1004
msgid "Loop in folder tree"
msgstr "Boucle dans l'arborescence des dossiers"
-#: core/models.py:925
+#: core/models.py:1007
msgid "You can not make a file be a children of a non folder file"
msgstr ""
"Vous ne pouvez pas mettre un fichier enfant de quelque chose qui n'est pas "
"un dossier"
-#: core/models.py:936
+#: core/models.py:1018
msgid "Duplicate file"
msgstr "Un fichier de ce nom existe déjà"
-#: core/models.py:953
+#: core/models.py:1035
msgid "You must provide a file"
msgstr "Vous devez fournir un fichier"
-#: core/models.py:1093
-msgid "Folder: "
-msgstr "Dossier : "
-
-#: core/models.py:1095
-msgid "File: "
-msgstr "Fichier : "
-
-#: core/models.py:1152
+#: core/models.py:1228
msgid "page unix name"
msgstr "nom unix de la page"
-#: core/models.py:1158
+#: core/models.py:1234
msgid ""
"Enter a valid page name. This value may contain only unaccented letters, "
"numbers and ./+/-/_ characters."
@@ -2324,55 +2328,55 @@ msgstr ""
"Entrez un nom de page correct. Uniquement des lettres non accentuées, "
"numéros, et ./+/-/_"
-#: core/models.py:1176
+#: core/models.py:1252
msgid "page name"
msgstr "nom de la page"
-#: core/models.py:1184
+#: core/models.py:1261
msgid "owner group"
msgstr "groupe propriétaire"
-#: core/models.py:1197
+#: core/models.py:1274
msgid "lock user"
msgstr "utilisateur bloquant"
-#: core/models.py:1204
+#: core/models.py:1281
msgid "lock_timeout"
msgstr "décompte du déblocage"
-#: core/models.py:1234
+#: core/models.py:1311
msgid "Duplicate page"
msgstr "Une page de ce nom existe déjà"
-#: core/models.py:1237
+#: core/models.py:1314
msgid "Loop in page tree"
msgstr "Boucle dans l'arborescence des pages"
-#: core/models.py:1397
+#: core/models.py:1474
msgid "revision"
msgstr "révision"
-#: core/models.py:1398
+#: core/models.py:1475
msgid "page title"
msgstr "titre de la page"
-#: core/models.py:1399
+#: core/models.py:1476
msgid "page content"
msgstr "contenu de la page"
-#: core/models.py:1443
+#: core/models.py:1520
msgid "url"
msgstr "url"
-#: core/models.py:1444
+#: core/models.py:1521
msgid "param"
msgstr "param"
-#: core/models.py:1449
+#: core/models.py:1526
msgid "viewed"
msgstr "vue"
-#: core/models.py:1507
+#: core/models.py:1586
msgid "operation type"
msgstr "type d'opération"
@@ -2392,24 +2396,18 @@ msgstr "500, Erreur Serveur"
msgid "Welcome!"
msgstr "Bienvenue !"
-#: core/templates/core/base.jinja:56
-msgid "Username"
-msgstr "Nom d'utilisateur"
-
-#: core/templates/core/base.jinja:58
-msgid "Password"
-msgstr "Mot de passe"
-
-#: core/templates/core/base.jinja:60 core/templates/core/login.jinja:4
+#: core/templates/core/base.jinja:53 core/templates/core/login.jinja:8
+#: core/templates/core/login.jinja:18 core/templates/core/login.jinja:50
#: core/templates/core/password_reset_complete.jinja:5
msgid "Login"
msgstr "Connexion"
-#: core/templates/core/base.jinja:62 core/templates/core/register.jinja:18
+#: core/templates/core/base.jinja:54 core/templates/core/register.jinja:7
+#: core/templates/core/register.jinja:16 core/templates/core/register.jinja:27
msgid "Register"
msgstr "Inscription"
-#: core/templates/core/base.jinja:91 core/templates/core/base.jinja:92
+#: core/templates/core/base.jinja:60 core/templates/core/base.jinja:61
#: forum/templates/forum/macros.jinja:171
#: forum/templates/forum/macros.jinja:175
#: matmat/templates/matmat/search_form.jinja:37
@@ -2419,24 +2417,28 @@ msgstr "Inscription"
msgid "Search"
msgstr "Recherche"
-#: core/templates/core/base.jinja:118
+#: core/templates/core/base.jinja:96
+msgid "Logout"
+msgstr "Déconnexion"
+
+#: core/templates/core/base.jinja:144
+msgid "You do not have any unread notification"
+msgstr "Vous n'avez aucune notification non lue"
+
+#: core/templates/core/base.jinja:149
msgid "View more"
msgstr "Voir plus"
-#: core/templates/core/base.jinja:122
+#: core/templates/core/base.jinja:152
#: forum/templates/forum/last_unread.jinja:17
msgid "Mark all as read"
msgstr "Marquer tout comme lu"
-#: core/templates/core/base.jinja:132
-msgid "Logout"
-msgstr "Déconnexion"
-
-#: core/templates/core/base.jinja:167
+#: core/templates/core/base.jinja:200
msgid "Main"
msgstr "Accueil"
-#: core/templates/core/base.jinja:169
+#: core/templates/core/base.jinja:202
msgid "Associations & Clubs"
msgstr "Associations & Clubs"
@@ -2456,11 +2458,11 @@ msgstr "Les autres associations de l'UTBM"
msgid "Elections"
msgstr "Élections"
-#: core/templates/core/base.jinja:188
+#: core/templates/core/base.jinja:213
msgid "Big event"
msgstr "Grandes Activités"
-#: core/templates/core/base.jinja:191
+#: core/templates/core/base.jinja:216
#: forum/templates/forum/favorite_topics.jinja:14
#: forum/templates/forum/last_unread.jinja:14
#: forum/templates/forum/macros.jinja:90 forum/templates/forum/main.jinja:6
@@ -2469,92 +2471,98 @@ msgstr "Grandes Activités"
msgid "Forum"
msgstr "Forum"
-#: core/templates/core/base.jinja:192
+#: core/templates/core/base.jinja:217
msgid "Gallery"
msgstr "Photos"
-#: core/templates/core/base.jinja:193 counter/models.py:379
+#: core/templates/core/base.jinja:218 counter/models.py:373
#: counter/templates/counter/counter_list.jinja:11
#: eboutic/templates/eboutic/eboutic_main.jinja:4
#: eboutic/templates/eboutic/eboutic_main.jinja:23
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:17
#: eboutic/templates/eboutic/eboutic_payment_result.jinja:4
-#: sith/settings.py:392 sith/settings.py:400
+#: sith/settings.py:397 sith/settings.py:405
msgid "Eboutic"
msgstr "Eboutic"
-#: core/templates/core/base.jinja:195
+#: core/templates/core/base.jinja:220
msgid "Services"
msgstr "Services"
-#: core/templates/core/base.jinja:199
+#: core/templates/core/base.jinja:222
msgid "Matmatronch"
msgstr "Matmatronch"
-#: core/templates/core/base.jinja:200 launderette/models.py:47
+#: core/templates/core/base.jinja:223 launderette/models.py:39
#: launderette/templates/launderette/launderette_book.jinja:5
#: launderette/templates/launderette/launderette_book_choose.jinja:4
#: launderette/templates/launderette/launderette_main.jinja:4
msgid "Launderette"
msgstr "Laverie"
-#: core/templates/core/base.jinja:201 core/templates/core/file.jinja:20
-#: core/views/files.py:86
+#: core/templates/core/base.jinja:224 core/templates/core/file.jinja:20
+#: core/views/files.py:110
msgid "Files"
msgstr "Fichiers"
-#: core/templates/core/base.jinja:202 core/templates/core/user_tools.jinja:109
+#: core/templates/core/base.jinja:225 core/templates/core/user_tools.jinja:171
msgid "Pedagogy"
msgstr "Pédagogie"
-#: core/templates/core/base.jinja:206
+#: core/templates/core/base.jinja:229
msgid "My Benefits"
msgstr "Mes Avantages"
-#: core/templates/core/base.jinja:210
+#: core/templates/core/base.jinja:231
msgid "Sponsors"
msgstr "Partenaires"
-#: core/templates/core/base.jinja:211
+#: core/templates/core/base.jinja:232
msgid "Subscriber benefits"
msgstr "Les avantages cotisants"
-#: core/templates/core/base.jinja:215
+#: core/templates/core/base.jinja:236
msgid "Help"
msgstr "Aide"
-#: core/templates/core/base.jinja:219
+#: core/templates/core/base.jinja:238
msgid "FAQ"
msgstr "FAQ"
-#: core/templates/core/base.jinja:220 core/templates/core/base.jinja:262
+#: core/templates/core/base.jinja:239 core/templates/core/base.jinja:279
msgid "Contacts"
msgstr "Contacts"
-#: core/templates/core/base.jinja:221
+#: core/templates/core/base.jinja:240
msgid "Wiki"
msgstr "Wiki"
-#: core/templates/core/base.jinja:263
+#: core/templates/core/base.jinja:280
msgid "Legal notices"
msgstr "Mentions légales"
-#: core/templates/core/base.jinja:264
+#: core/templates/core/base.jinja:281
msgid "Intellectual property"
msgstr "Propriété intellectuelle"
-#: core/templates/core/base.jinja:265
+#: core/templates/core/base.jinja:282
msgid "Help & Documentation"
msgstr "Aide & Documentation"
-#: core/templates/core/base.jinja:266
+#: core/templates/core/base.jinja:283
msgid "R&D"
msgstr "R&D"
-#: core/templates/core/base.jinja:262
+#: core/templates/core/base.jinja:286
msgid "Site created by the IT Department of the AE"
msgstr "Site réalisé par le Pôle Informatique de l'AE"
+#: core/templates/core/base.jinja:292
+#, fuzzy
+#| msgid "Site version:"
+msgid "Sith version:"
+msgstr "Version du site :"
+
#: core/templates/core/create.jinja:4 core/templates/core/create.jinja:8
#, python-format
msgid "Create %(name)s"
@@ -2613,38 +2621,35 @@ msgstr "Propriétés"
#: core/templates/core/file_detail.jinja:13
#: core/templates/core/file_moderation.jinja:20
-#: sas/templates/sas/picture.jinja:88
+#: sas/templates/sas/picture.jinja:86
msgid "Owner: "
msgstr "Propriétaire : "
-#: core/templates/core/file_detail.jinja:26 sas/templates/sas/album.jinja:28
+#: core/templates/core/file_detail.jinja:26 sas/templates/sas/album.jinja:51
+#: sas/templates/sas/main.jinja:75
msgid "Clear clipboard"
msgstr "Vider le presse-papier"
-#: core/templates/core/file_detail.jinja:27 sas/templates/sas/album.jinja:29
+#: core/templates/core/file_detail.jinja:27 sas/templates/sas/album.jinja:38
msgid "Cut"
msgstr "Couper"
-#: core/templates/core/file_detail.jinja:28 sas/templates/sas/album.jinja:30
+#: core/templates/core/file_detail.jinja:28 sas/templates/sas/album.jinja:39
msgid "Paste"
msgstr "Coller"
-#: core/templates/core/file_detail.jinja:31 sas/templates/sas/album.jinja:33
+#: core/templates/core/file_detail.jinja:31 sas/templates/sas/album.jinja:45
+#: sas/templates/sas/main.jinja:69
msgid "Clipboard: "
msgstr "Presse-papier : "
-#: sas/templates/sas/album.jinja:69
-#: sas/templates/sas/album.jinja:97
-msgid "To be moderated"
-msgstr "A modérer"
-
#: core/templates/core/file_detail.jinja:53
msgid "Real name: "
msgstr "Nom réel : "
#: core/templates/core/file_detail.jinja:54
#: core/templates/core/file_moderation.jinja:21
-#: sas/templates/sas/picture.jinja:87
+#: sas/templates/sas/picture.jinja:82
msgid "Date: "
msgstr "Date : "
@@ -2695,8 +2700,8 @@ msgid "Edit group"
msgstr "Éditer le groupe"
#: core/templates/core/group_edit.jinja:9
-#: core/templates/core/user_edit.jinja:37
-#: core/templates/core/user_group.jinja:8
+#: core/templates/core/user_edit.jinja:138
+#: core/templates/core/user_group.jinja:13
#: pedagogy/templates/pedagogy/uv_edit.jinja:36
msgid "Update"
msgstr "Mettre à jour"
@@ -2718,34 +2723,33 @@ msgstr "ID"
msgid "Group"
msgstr "Groupe"
-#: core/templates/core/login.jinja:10
+#: core/templates/core/login.jinja:22
+#, fuzzy
+#| msgid ""
+#| "Your account doesn't have access to this page. To proceed,\n"
+#| " please login with an account that has access."
+msgid ""
+"Your account doesn't have access to this page. To proceed,\n"
+" please login with an account that has access."
+msgstr ""
+"Votre compte n'a pas accès à cette page. Merci de vous identifier avec un "
+"compte qui a accès."
+
+#: core/templates/core/login.jinja:25
+msgid "Please login or create an account to see this page."
+msgstr "Merci de vous identifier ou de créer un compte pour voir cette page."
+
+#: core/templates/core/login.jinja:31
msgid "Your username and password didn't match. Please try again."
msgstr ""
"Votre nom d'utilisateur et votre mot de passe ne correspondent pas. Merci de "
"réessayer."
-#: core/templates/core/login.jinja:15
-msgid ""
-"Your account doesn't have access to this page. To proceed,\n"
-" please login with an account that has access."
-msgstr ""
-"Votre compte n'a pas accès à cette page. Merci de vous identifier avec un "
-"compte qui a accès."
-
-#: core/templates/core/login.jinja:18
-msgid "Please login or create an account to see this page."
-msgstr "Merci de vous identifier ou de créer un compte pour voir cette page."
-
-#: core/templates/core/login.jinja:28
-#: counter/templates/counter/counter_main.jinja:56
-msgid "login"
-msgstr "login"
-
-#: core/templates/core/login.jinja:32
+#: core/templates/core/login.jinja:55
msgid "Lost password?"
msgstr "Mot de passe perdu ?"
-#: core/templates/core/login.jinja:33
+#: core/templates/core/login.jinja:57
msgid "Create account"
msgstr "Créer un compte"
@@ -2762,11 +2766,11 @@ msgstr "Tweeter"
msgid "Subscribed until %(subscription_end)s"
msgstr "Cotisant jusqu'au %(subscription_end)s"
-#: core/templates/core/macros.jinja:86 core/templates/core/user_edit.jinja:40
+#: core/templates/core/macros.jinja:86
msgid "Account number: "
msgstr "Numéro de compte : "
-#: core/templates/core/macros.jinja:91 launderette/models.py:217
+#: core/templates/core/macros.jinja:91 launderette/models.py:215
msgid "Slot"
msgstr "Créneau"
@@ -2977,23 +2981,19 @@ msgstr "Merci d'utiliser notre site !"
msgid "The %(site_name)s team"
msgstr "L'équipe de %(site_name)s"
-#: core/templates/core/register.jinja:3 core/templates/core/register.jinja:6
-msgid "Register a user"
-msgstr "Enregistrer un utilisateur"
-
-#: core/templates/core/register.jinja:9
+#: core/templates/core/register.jinja:19
#, python-format
msgid "Welcome %(user_name)s!"
msgstr "Bienvenue, %(user_name)s!"
-#: core/templates/core/register.jinja:10
+#: core/templates/core/register.jinja:20
msgid ""
"You successfully registered and you will soon receive a confirmation mail."
msgstr ""
"Vous vous êtes correctement enregistré, et vous devriez recevoir rapidement "
"un email de confirmation."
-#: core/templates/core/register.jinja:12
+#: core/templates/core/register.jinja:21
#, python-format
msgid "Your username is %(username)s."
msgstr "Votre nom d'utilisateur est %(username)s."
@@ -3006,7 +3006,7 @@ msgstr "Résultat de la recherche"
msgid "Users"
msgstr "Utilisateurs"
-#: core/templates/core/search.jinja:18 core/views/user.py:242
+#: core/templates/core/search.jinja:18 core/views/user.py:241
msgid "Clubs"
msgstr "Clubs"
@@ -3063,7 +3063,7 @@ msgid "Eboutic invoices"
msgstr "Facture eboutic"
#: core/templates/core/user_account.jinja:57
-#: core/templates/core/user_tools.jinja:37 counter/views.py:807
+#: core/templates/core/user_tools.jinja:58 counter/views.py:801
msgid "Etickets"
msgstr "Etickets"
@@ -3089,108 +3089,106 @@ msgstr "Clubs"
msgid "Current club(s) :"
msgstr "Clubs actuels : "
-#: core/templates/core/user_clubs.jinja:39
+#: core/templates/core/user_clubs.jinja:41
msgid "Old club(s) :"
msgstr "Anciens clubs :"
-#: core/templates/core/user_clubs.jinja:69
+#: core/templates/core/user_clubs.jinja:74
msgid "Subscribed mailing lists"
msgstr "Mailing listes abonnées"
-#: core/templates/core/user_clubs.jinja:71
+#: core/templates/core/user_clubs.jinja:76
msgid "Unsubscribe"
msgstr "Se désabonner"
-#: core/templates/core/user_detail.jinja:5
+#: core/templates/core/user_detail.jinja:9
#, python-format
msgid "%(user_name)s's profile"
msgstr "Profil de %(user_name)s"
-#: core/templates/core/user_detail.jinja:29
+#: core/templates/core/user_detail.jinja:38
msgid "Pronouns: "
msgstr "Pronoms : "
-#: core/templates/core/user_detail.jinja:35
+#: core/templates/core/user_detail.jinja:44
msgid "Born: "
msgstr "Né le : "
-#: core/templates/core/user_detail.jinja:42
+#: core/templates/core/user_detail.jinja:51
msgid "Department: "
msgstr "Département : "
-#: core/templates/core/user_detail.jinja:49
+#: core/templates/core/user_detail.jinja:59
msgid "Option: "
msgstr "Filière : "
-#: core/templates/core/user_detail.jinja:56
+#: core/templates/core/user_detail.jinja:66
#: trombi/templates/trombi/export.jinja:20
msgid "Phone: "
msgstr "Téléphone : "
-#: core/templates/core/user_detail.jinja:63
+#: core/templates/core/user_detail.jinja:73
msgid "Address: "
msgstr "Adresse : "
-#: core/templates/core/user_detail.jinja:70
+#: core/templates/core/user_detail.jinja:80
msgid "Parents address: "
msgstr "Adresse des parents : "
-#: core/templates/core/user_detail.jinja:79
+#: core/templates/core/user_detail.jinja:89
msgid "Promo: "
msgstr "Promo : "
-#: core/templates/core/user_detail.jinja:104
-#: core/templates/core/user_detail.jinja:105
-#: core/templates/core/user_detail.jinja:107
-#: core/templates/core/user_detail.jinja:108
-#: core/templates/core/user_edit.jinja:31
+#: core/templates/core/user_detail.jinja:121
+#: core/templates/core/user_detail.jinja:122
+#: core/templates/core/user_detail.jinja:124
+#: core/templates/core/user_detail.jinja:125
msgid "Avatar"
msgstr "Avatar"
-#: core/templates/core/user_detail.jinja:112
-#: core/templates/core/user_detail.jinja:113
-#: core/templates/core/user_detail.jinja:115
-#: core/templates/core/user_detail.jinja:116
-#: core/templates/core/user_edit.jinja:34
+#: core/templates/core/user_detail.jinja:129
+#: core/templates/core/user_detail.jinja:130
+#: core/templates/core/user_detail.jinja:132
+#: core/templates/core/user_detail.jinja:133
msgid "Scrub"
msgstr "Blouse"
-#: core/templates/core/user_detail.jinja:141
+#: core/templates/core/user_detail.jinja:163
msgid "Not subscribed"
msgstr "Non cotisant"
-#: core/templates/core/user_detail.jinja:143
+#: core/templates/core/user_detail.jinja:166
#: subscription/templates/subscription/subscription.jinja:4
#: subscription/templates/subscription/subscription.jinja:8
msgid "New subscription"
msgstr "Nouvelle cotisation"
-#: core/templates/core/user_detail.jinja:150
+#: core/templates/core/user_detail.jinja:177
msgid "Subscription history"
msgstr "Historique de cotisation"
-#: core/templates/core/user_detail.jinja:153
+#: core/templates/core/user_detail.jinja:187
msgid "Subscription start"
msgstr "Début de la cotisation"
-#: core/templates/core/user_detail.jinja:154
+#: core/templates/core/user_detail.jinja:188
msgid "Subscription end"
msgstr "Fin de la cotisation"
-#: core/templates/core/user_detail.jinja:155
+#: core/templates/core/user_detail.jinja:189
#: subscription/templates/subscription/stats.jinja:36
msgid "Subscription type"
msgstr "Type de cotisation"
-#: core/templates/core/user_detail.jinja:177
+#: core/templates/core/user_detail.jinja:213
msgid "Give gift"
msgstr "Donner cadeau"
-#: core/templates/core/user_detail.jinja:182
+#: core/templates/core/user_detail.jinja:221
msgid "Last given gift :"
msgstr "Dernier cadeau donné :"
-#: core/templates/core/user_detail.jinja:192
+#: core/templates/core/user_detail.jinja:239
msgid "No gift given yet"
msgstr "Aucun cadeau donné pour l'instant"
@@ -3198,73 +3196,65 @@ msgstr "Aucun cadeau donné pour l'instant"
msgid "Edit user"
msgstr "Éditer l'utilisateur"
-#: core/templates/core/user_edit.jinja:8
+#: core/templates/core/user_edit.jinja:11
msgid "Edit user profile"
msgstr "Éditer le profil de l'utilisateur"
-#: core/templates/core/user_edit.jinja:15
-msgid "Current profile: "
-msgstr "Profil actuel : "
-
-#: core/templates/core/user_edit.jinja:25
-msgid "Take picture"
-msgstr "Prendre une photo"
-
-#: core/templates/core/user_edit.jinja:30
-msgid "Current avatar: "
-msgstr "Avatar actuel : "
-
-#: core/templates/core/user_edit.jinja:33
-msgid "Current scrub: "
-msgstr "Blouse actuelle : "
-
-#: core/templates/core/user_edit.jinja:38
-msgid "Username: "
-msgstr "Nom d'utilisateur : "
-
-#: core/templates/core/user_edit.jinja:43
-msgid "Change my password"
-msgstr "Changer mon mot de passe"
-
-#: core/templates/core/user_edit.jinja:45
-msgid "Change user password"
-msgstr "Changer le mot de passe"
-
-#: core/templates/core/user_edit.jinja:50
+#: core/templates/core/user_edit.jinja:35
msgid "To edit your profile picture, ask a member of the AE"
msgstr "Pour changer votre photo de profil, demandez à un membre de l'AE"
-#: core/templates/core/user_godfathers.jinja:5
+#: core/templates/core/user_edit.jinja:128
+msgid "Change my password"
+msgstr "Changer mon mot de passe"
+
+#: core/templates/core/user_edit.jinja:133
+msgid "Change user password"
+msgstr "Changer le mot de passe"
+
+#: core/templates/core/user_edit.jinja:143
+#, fuzzy
+#| msgid "Username: "
+msgid "Username:"
+msgstr "Nom d'utilisateur : "
+
+#: core/templates/core/user_edit.jinja:146
+#, fuzzy
+#| msgid "Account number: "
+msgid "Account number:"
+msgstr "Numéro de compte : "
+
+#: core/templates/core/user_godfathers.jinja:9
#, python-format
msgid "%(user_name)s's family"
msgstr "Famille de %(user_name)s"
-#: core/templates/core/user_godfathers.jinja:10
+#: core/templates/core/user_godfathers.jinja:15
msgid "Show family picture"
msgstr "Voir une image de la famille"
-#: core/templates/core/user_godfathers.jinja:12
+#: core/templates/core/user_godfathers.jinja:18
msgid "Godfathers / Godmothers"
msgstr "Parrains / Marraines"
-#: core/templates/core/user_godfathers.jinja:20
+#: core/templates/core/user_godfathers.jinja:32
msgid "Show ancestors tree"
msgstr "Voir l'arbre des ancêtres"
-#: core/templates/core/user_godfathers.jinja:22
+#: core/templates/core/user_godfathers.jinja:35
#: core/templates/core/user_godfathers_tree.jinja:50
msgid "No godfathers / godmothers"
msgstr "Pas de famille"
-#: core/templates/core/user_godfathers.jinja:25 core/views/user.py:462
+#: core/templates/core/user_godfathers.jinja:38 core/views/user.py:463
msgid "Godchildren"
msgstr "Fillots / Fillotes"
-#: core/templates/core/user_godfathers.jinja:33
+#: core/templates/core/user_godfathers.jinja:52
msgid "Show descent tree"
msgstr "Voir l'arbre de la descendance"
-#: core/templates/core/user_godfathers.jinja:35
+#: core/templates/core/user_godfathers.jinja:55
#: core/templates/core/user_godfathers_tree.jinja:48
msgid "No godchildren"
msgstr "Pas de fillots / fillotes"
@@ -3302,7 +3292,7 @@ msgstr "Descendants de %(u)s"
msgid "Ancestors tree of %(u)s"
msgstr "Ancêtres de %(u)s"
-#: core/templates/core/user_group.jinja:4
+#: core/templates/core/user_group.jinja:9
#, python-format
msgid "Edit user groups for %(user_name)s"
msgstr "Éditer les groupes pour %(user_name)s"
@@ -3311,240 +3301,257 @@ msgstr "Éditer les groupes pour %(user_name)s"
msgid "User list"
msgstr "Liste d'utilisateurs"
-#: core/templates/core/user_pictures.jinja:4
+#: core/templates/core/user_pictures.jinja:8
#, python-format
msgid "%(user_name)s's pictures"
msgstr "Photos de %(user_name)s"
-#: core/templates/core/user_pictures.jinja:9
+#: core/templates/core/user_pictures.jinja:14
msgid "Download all my pictures"
msgstr "Télécharger toutes mes photos"
-#: core/templates/core/user_pictures.jinja:83
-msgid "Error downloading your pictures"
-msgstr "Erreur de téléchargement de vos photos"
+#: core/templates/core/user_pictures.jinja:28 sas/templates/sas/album.jinja:68
+#: sas/templates/sas/album.jinja:96
+msgid "To be moderated"
+msgstr "A modérer"
-#: core/templates/core/user_picture.jinja:
+#: core/templates/core/user_pictures.jinja:37
msgid "Picture Unavailable"
msgstr "Photo Indisponible"
-#: core/templates/core/user_preferences.jinja:4
-#: core/templates/core/user_preferences.jinja:8 core/views/user.py:234
+#: core/templates/core/user_pictures.jinja:105
+msgid "Error downloading your pictures"
+msgstr "Erreur de téléchargement de vos photos"
+
+#: core/templates/core/user_preferences.jinja:8
+#: core/templates/core/user_preferences.jinja:13 core/views/user.py:233
msgid "Preferences"
msgstr "Préférences"
-#: core/templates/core/user_preferences.jinja:14 trombi/views.py:58
+#: core/templates/core/user_preferences.jinja:14
+#, fuzzy
+#| msgid "Generate"
+msgid "General"
+msgstr "Générer"
+
+#: core/templates/core/user_preferences.jinja:21 trombi/views.py:57
msgid "Trombi"
msgstr "Trombi"
-#: core/templates/core/user_preferences.jinja:22
+#: core/templates/core/user_preferences.jinja:31
#, python-format
msgid "You already choose to be in that Trombi: %(trombi)s."
msgstr "Vous avez déjà choisi ce Trombi: %(trombi)s."
-#: core/templates/core/user_preferences.jinja:23
+#: core/templates/core/user_preferences.jinja:33
msgid "Go to my Trombi tools"
msgstr "Allez à mes outils de Trombi"
-#: core/templates/core/user_preferences.jinja:26
+#: core/templates/core/user_preferences.jinja:39
msgid "Student cards"
msgstr "Cartes étudiante"
-#: core/templates/core/user_preferences.jinja:27
+#: core/templates/core/user_preferences.jinja:54
+msgid "No student card registered."
+msgstr "Aucune carte étudiante enregistrée."
+
+#: core/templates/core/user_preferences.jinja:56
+#, fuzzy
+#| msgid ""
+#| "You can add a card by asking at a counter or add it yourself here. If you "
+#| "want to manually add a student card yourself, you'll need a NFC reader. "
+#| "We store the UID of the card which is 14 characters long."
msgid ""
"You can add a card by asking at a counter or add it yourself here. If you "
-"want to manually add a student card yourself, you'll need a NFC reader. We "
-"store the UID of the card which is 14 characters long."
+"want to manually\n"
+" add a student card yourself, you'll need a NFC reader. "
+"We store the UID of the card which is 14 characters long."
msgstr ""
"Vous pouvez ajouter une carte en demandant à un comptoir ou en l'ajoutant "
"vous même ici. Si vous voulez l'ajouter manuellement par vous même, vous "
"aurez besoin d'un lecteur NFC. Nous enregistrons l'UID de la carte qui fait "
"14 caractères de long."
-#: core/templates/core/user_preferences.jinja:40
-msgid "No student card registered."
-msgstr "Aucune carte étudiante enregistrée."
-
-#: core/templates/core/user_stats.jinja:4
+#: core/templates/core/user_stats.jinja:8
#, python-format
msgid "%(user_name)s's stats"
msgstr "Stats de %(user_name)s"
-#: core/templates/core/user_stats.jinja:9
+#: core/templates/core/user_stats.jinja:16
msgid "Permanencies"
msgstr "Permanences"
-#: core/templates/core/user_stats.jinja:17
+#: core/templates/core/user_stats.jinja:27
msgid "Buyings"
msgstr "Achats"
-#: core/templates/core/user_stats.jinja:23
+#: core/templates/core/user_stats.jinja:39
msgid "Product top 10"
msgstr "Top 10 produits"
-#: core/templates/core/user_stats.jinja:27 counter/forms.py:176
+#: core/templates/core/user_stats.jinja:43 counter/forms.py:176
msgid "Product"
msgstr "Produit"
-#: core/templates/core/user_tools.jinja:4
+#: core/templates/core/user_tools.jinja:8
#, python-format
msgid "%(user_name)s's tools"
msgstr "Outils de %(user_name)s"
-#: core/templates/core/user_tools.jinja:8
+#: core/templates/core/user_tools.jinja:13
msgid "User Tools"
msgstr "Outils utilisateurs"
-#: core/templates/core/user_tools.jinja:11
+#: core/templates/core/user_tools.jinja:18
msgid "Sith management"
msgstr "Gestion de Sith"
-#: core/templates/core/user_tools.jinja:14 core/views/user.py:250
+#: core/templates/core/user_tools.jinja:21 core/views/user.py:249
msgid "Groups"
msgstr "Groupes"
-#: core/templates/core/user_tools.jinja:15
+#: core/templates/core/user_tools.jinja:22
#: rootplace/templates/rootplace/merge.jinja:4
msgid "Merge users"
msgstr "Fusionner deux utilisateurs"
-#: core/templates/core/user_tools.jinja:16
+#: core/templates/core/user_tools.jinja:23
#: rootplace/templates/rootplace/logs.jinja:5
msgid "Operation logs"
msgstr "Journal d'opérations"
-#: core/templates/core/user_tools.jinja:17
+#: core/templates/core/user_tools.jinja:24
#: rootplace/templates/rootplace/delete_user_messages.jinja:4
msgid "Delete user's forum messages"
msgstr "Supprimer les messages forum d'un utilisateur"
-#: core/templates/core/user_tools.jinja:20
+#: core/templates/core/user_tools.jinja:27
msgid "Subscriptions"
msgstr "Cotisations"
-#: core/templates/core/user_tools.jinja:23
+#: core/templates/core/user_tools.jinja:30
#: subscription/templates/subscription/stats.jinja:4
msgid "Subscription stats"
msgstr "Statistiques de cotisation"
-#: core/templates/core/user_tools.jinja:29 counter/forms.py:139
-#: counter/views.py:777
+#: core/templates/core/user_tools.jinja:48 counter/forms.py:139
+#: counter/views.py:771
msgid "Counters"
msgstr "Comptoirs"
-#: core/templates/core/user_tools.jinja:32
+#: core/templates/core/user_tools.jinja:53
msgid "General counters management"
msgstr "Gestion générale des comptoirs"
-#: core/templates/core/user_tools.jinja:33
+#: core/templates/core/user_tools.jinja:54
msgid "Products management"
msgstr "Gestion des produits"
-#: core/templates/core/user_tools.jinja:34
+#: core/templates/core/user_tools.jinja:55
msgid "Product types management"
msgstr "Gestion des types de produit"
-#: core/templates/core/user_tools.jinja:35
-#: counter/templates/counter/cash_summary_list.jinja:23 counter/views.py:797
+#: core/templates/core/user_tools.jinja:56
+#: counter/templates/counter/cash_summary_list.jinja:23 counter/views.py:791
msgid "Cash register summaries"
msgstr "Relevés de caisse"
-#: core/templates/core/user_tools.jinja:36
-#: counter/templates/counter/invoices_call.jinja:4 counter/views.py:802
+#: core/templates/core/user_tools.jinja:57
+#: counter/templates/counter/invoices_call.jinja:4 counter/views.py:796
msgid "Invoices call"
msgstr "Appels à facture"
-#: core/templates/core/user_tools.jinja:44 core/views/user.py:268
+#: core/templates/core/user_tools.jinja:72 core/views/user.py:268
#: counter/templates/counter/counter_list.jinja:18
#: counter/templates/counter/counter_list.jinja:34
#: counter/templates/counter/counter_list.jinja:56
msgid "Stats"
msgstr "Stats"
-#: core/templates/core/user_tools.jinja:48
+#: core/templates/core/user_tools.jinja:78
#: counter/templates/counter/counter_list.jinja:38
#: stock/templates/stock/stock_item_list.jinja:11
#: stock/templates/stock/stock_list.jinja:16
msgid "Shopping lists"
msgstr "Liste de courses"
-#: core/templates/core/user_tools.jinja:50
+#: core/templates/core/user_tools.jinja:80
#: counter/templates/counter/counter_list.jinja:40
msgid "Create new stock"
msgstr "Créer nouveau stock"
-#: core/templates/core/user_tools.jinja:61
+#: core/templates/core/user_tools.jinja:101
msgid "Refound Account"
msgstr "Rembourser un compte"
-#: core/templates/core/user_tools.jinja:62
+#: core/templates/core/user_tools.jinja:102
msgid "General accounting"
msgstr "Comptabilité générale"
-#: core/templates/core/user_tools.jinja:72
+#: core/templates/core/user_tools.jinja:117
msgid "Club account: "
msgstr "Compte club : "
-#: core/templates/core/user_tools.jinja:79
+#: core/templates/core/user_tools.jinja:133
msgid "Communication"
msgstr "Communication"
-#: core/templates/core/user_tools.jinja:82
+#: core/templates/core/user_tools.jinja:136
msgid "Create weekmail article"
msgstr "Rédiger un nouvel article dans le Weekmail"
-#: core/templates/core/user_tools.jinja:86
+#: core/templates/core/user_tools.jinja:140
msgid "Moderate news"
msgstr "Modérer les nouvelles"
-#: core/templates/core/user_tools.jinja:87
+#: core/templates/core/user_tools.jinja:141
msgid "Edit alert message"
msgstr "Éditer le message d'alerte"
-#: core/templates/core/user_tools.jinja:88
+#: core/templates/core/user_tools.jinja:142
msgid "Edit information message"
msgstr "Éditer le message d'informations"
-#: core/templates/core/user_tools.jinja:89
+#: core/templates/core/user_tools.jinja:143
msgid "Moderate files"
msgstr "Modérer les fichiers"
-#: core/templates/core/user_tools.jinja:95
+#: core/templates/core/user_tools.jinja:149
msgid "Moderate pictures"
msgstr "Modérer les photos"
-#: core/templates/core/user_tools.jinja:112
+#: core/templates/core/user_tools.jinja:173
#: pedagogy/templates/pedagogy/guide.jinja:20
msgid "Create UV"
msgstr "Créer UV"
-#: core/templates/core/user_tools.jinja:113
+#: core/templates/core/user_tools.jinja:174
#: pedagogy/templates/pedagogy/guide.jinja:23
#: trombi/templates/trombi/detail.jinja:10
msgid "Moderate comments"
msgstr "Modérer les commentaires"
-#: core/templates/core/user_tools.jinja:120
+#: core/templates/core/user_tools.jinja:182
msgid "See available elections"
msgstr "Voir les élections disponibles"
-#: core/templates/core/user_tools.jinja:121
+#: core/templates/core/user_tools.jinja:183
msgid "See archived elections"
msgstr "Voir les élections archivées"
-#: core/templates/core/user_tools.jinja:123
+#: core/templates/core/user_tools.jinja:185
msgid "Create a new election"
msgstr "Créer une nouvelle élection"
-#: core/templates/core/user_tools.jinja:128
+#: core/templates/core/user_tools.jinja:191
msgid "Other tools"
msgstr "Autres outils"
-#: core/templates/core/user_tools.jinja:130
+#: core/templates/core/user_tools.jinja:193
msgid "Convert dokuwiki/BBcode syntax to Markdown"
msgstr "Convertir de la syntaxe dokuwiki/BBcode vers Markdown"
-#: core/templates/core/user_tools.jinja:131
+#: core/templates/core/user_tools.jinja:194
msgid "Trombi tools"
msgstr "Outils Trombi"
@@ -3555,110 +3562,110 @@ msgid_plural "%(nb_days)d days, %(remainder)s"
msgstr[0] ""
msgstr[1] ""
-#: core/views/files.py:82
+#: core/views/files.py:107
msgid "Add a new folder"
msgstr "Ajouter un nouveau dossier"
-#: core/views/files.py:103
+#: core/views/files.py:127
#, python-format
msgid "Error creating folder %(folder_name)s: %(msg)s"
msgstr "Erreur de création du dossier %(folder_name)s : %(msg)s"
-#: core/views/files.py:123 core/views/forms.py:310 core/views/forms.py:317
-#: sas/views.py:94
+#: core/views/files.py:147 core/views/forms.py:308 core/views/forms.py:315
+#: sas/views.py:83
#, python-format
msgid "Error uploading file %(file_name)s: %(msg)s"
msgstr "Erreur d'envoi du fichier %(file_name)s : %(msg)s"
-#: core/views/files.py:198 sas/views.py:378
+#: core/views/files.py:229 sas/views.py:367
msgid "Apply rights recursively"
msgstr "Appliquer les droits récursivement"
-#: core/views/forms.py:91
+#: core/views/forms.py:88
msgid "Heading"
msgstr "Titre"
-#: core/views/forms.py:92
+#: core/views/forms.py:89
msgid "Italic"
msgstr "Italique"
-#: core/views/forms.py:93
+#: core/views/forms.py:90
msgid "Bold"
msgstr "Gras"
-#: core/views/forms.py:94
+#: core/views/forms.py:91
msgid "Strikethrough"
msgstr "Barré"
-#: core/views/forms.py:95
+#: core/views/forms.py:92
msgid "Underline"
msgstr "Souligné"
-#: core/views/forms.py:96
+#: core/views/forms.py:93
msgid "Superscript"
msgstr "Exposant"
-#: core/views/forms.py:97
+#: core/views/forms.py:94
msgid "Subscript"
msgstr "Indice"
-#: core/views/forms.py:99
+#: core/views/forms.py:96
msgid "Quote"
msgstr "Citation"
-#: core/views/forms.py:100
+#: core/views/forms.py:97
msgid "Unordered list"
msgstr "Liste non ordonnée"
-#: core/views/forms.py:101
+#: core/views/forms.py:98
msgid "Ordered list"
msgstr "Liste ordonnée"
-#: core/views/forms.py:102
+#: core/views/forms.py:99
msgid "Insert image"
msgstr "Insérer image"
-#: core/views/forms.py:103
+#: core/views/forms.py:100
msgid "Insert link"
msgstr "Insérer lien"
-#: core/views/forms.py:104
+#: core/views/forms.py:101
msgid "Insert table"
msgstr "Insérer tableau"
-#: core/views/forms.py:105
+#: core/views/forms.py:102
msgid "Clean block"
msgstr "Nettoyer bloc"
-#: core/views/forms.py:106
+#: core/views/forms.py:103
msgid "Toggle preview"
msgstr "Activer la prévisualisation"
-#: core/views/forms.py:107
+#: core/views/forms.py:104
msgid "Toggle side by side"
msgstr "Activer la vue côte à côte"
-#: core/views/forms.py:108
+#: core/views/forms.py:105
msgid "Toggle fullscreen"
msgstr "Activer le plein écran"
-#: core/views/forms.py:109
+#: core/views/forms.py:106
msgid "Markdown guide"
msgstr "Guide markdown"
-#: core/views/forms.py:125 core/views/forms.py:133
+#: core/views/forms.py:122 core/views/forms.py:130
msgid "Choose file"
msgstr "Choisir un fichier"
-#: core/views/forms.py:149 core/views/forms.py:157
+#: core/views/forms.py:146 core/views/forms.py:154
msgid "Choose user"
msgstr "Choisir un utilisateur"
-#: core/views/forms.py:189
+#: core/views/forms.py:186
msgid "Username, email, or account number"
msgstr "Nom d'utilisateur, email, ou numéro de compte AE"
-#: core/views/forms.py:256
+#: core/views/forms.py:254
msgid ""
"Profile: you need to be visible on the picture, in order to be recognized (e."
"g. by the barmen)"
@@ -3666,67 +3673,68 @@ msgstr ""
"Photo de profil: vous devez être visible sur la photo afin d'être reconnu "
"(par exemple par les barmen)"
-#: core/views/forms.py:258
+#: core/views/forms.py:256
msgid "Avatar: used on the forum"
msgstr "Avatar : utilisé sur le forum"
-#: core/views/forms.py:259
+#: core/views/forms.py:257
msgid "Scrub: let other know how your scrub looks like!"
msgstr "Blouse : montrez aux autres à quoi ressemble votre blouse !"
-#: core/views/forms.py:321
+#: core/views/forms.py:319
msgid "Bad image format, only jpeg, png, and gif are accepted"
msgstr "Mauvais format d'image, seuls les jpeg, png, et gif sont acceptés"
-#: core/views/forms.py:342
+#: core/views/forms.py:340
msgid "Godfather / Godmother"
msgstr "Parrain / Marraine"
-#: core/views/forms.py:343
+#: core/views/forms.py:341
msgid "Godchild"
msgstr "Fillot / Fillote"
-#: core/views/forms.py:348 counter/forms.py:55 trombi/views.py:158
+#: core/views/forms.py:346 counter/forms.py:55 trombi/views.py:156
msgid "Select user"
msgstr "Choisir un utilisateur"
-#: core/views/forms.py:361 core/views/forms.py:379 election/models.py:24
-#: election/views.py:155
+#: core/views/forms.py:359 core/views/forms.py:377 election/models.py:24
+#: election/views.py:150
msgid "edit groups"
msgstr "groupe d'édition"
-#: core/views/forms.py:364 core/views/forms.py:382 election/models.py:31
-#: election/views.py:158
+#: core/views/forms.py:362 core/views/forms.py:380 election/models.py:31
+#: election/views.py:153
msgid "view groups"
msgstr "groupe de vue"
-#: core/views/group.py:55
+#: core/views/group.py:44
msgid "Users to remove from group"
msgstr "Utilisateurs à retirer du groupe"
-#: core/views/group.py:62
+#: core/views/group.py:51
msgid "Users to add to group"
msgstr "Utilisateurs à ajouter au groupe"
-#: core/views/user.py:202 core/views/user.py:464 core/views/user.py:466
+#: core/views/user.py:201 core/views/user.py:465 core/views/user.py:467
msgid "Family"
msgstr "Famille"
-#: core/views/user.py:207 trombi/templates/trombi/export.jinja:25
+#: core/views/user.py:206 sas/templates/sas/album.jinja:84
+#: trombi/templates/trombi/export.jinja:25
#: trombi/templates/trombi/user_profile.jinja:11
msgid "Pictures"
msgstr "Photos"
-#: core/views/user.py:215
+#: core/views/user.py:214
msgid "Galaxy"
msgstr "Galaxie"
-#: core/views/user.py:608
+#: core/views/user.py:612
msgid "User already has a profile picture"
msgstr "L'utilisateur a déjà une photo de profil"
-#: counter/app.py:31 counter/models.py:395 counter/models.py:875
-#: counter/models.py:911 launderette/models.py:41 stock/models.py:43
+#: counter/app.py:31 counter/models.py:389 counter/models.py:880
+#: counter/models.py:916 launderette/models.py:33 stock/models.py:42
msgid "counter"
msgstr "comptoir"
@@ -3750,165 +3758,165 @@ msgstr "Groupes d'achat"
msgid "Ecocup regularization"
msgstr "Régularization des ecocups"
-#: counter/models.py:63
+#: counter/models.py:53
msgid "account id"
msgstr "numéro de compte"
-#: counter/models.py:65
+#: counter/models.py:55
msgid "recorded product"
msgstr "produits consignés"
-#: counter/models.py:68
+#: counter/models.py:58
msgid "customer"
msgstr "client"
-#: counter/models.py:69
+#: counter/models.py:59
msgid "customers"
msgstr "clients"
-#: counter/models.py:148 counter/views.py:319
+#: counter/models.py:138 counter/views.py:316
msgid "Not enough money"
msgstr "Solde insuffisant"
-#: counter/models.py:183
+#: counter/models.py:173
msgid "First name"
msgstr "Prénom"
-#: counter/models.py:184
+#: counter/models.py:174
msgid "Last name"
msgstr "Nom de famille"
-#: counter/models.py:185
+#: counter/models.py:175
msgid "Address 1"
msgstr "Adresse 1"
-#: counter/models.py:186
+#: counter/models.py:176
msgid "Address 2"
msgstr "Adresse 2"
-#: counter/models.py:187
+#: counter/models.py:177
msgid "Zip code"
msgstr "Code postal"
-#: counter/models.py:188
+#: counter/models.py:178
msgid "City"
msgstr "Ville"
-#: counter/models.py:189
+#: counter/models.py:179
msgid "Country"
msgstr "Pays"
-#: counter/models.py:232 counter/models.py:260
+#: counter/models.py:222 counter/models.py:252
msgid "product type"
msgstr "type du produit"
-#: counter/models.py:266
+#: counter/models.py:258
msgid "purchase price"
msgstr "prix d'achat"
-#: counter/models.py:267
+#: counter/models.py:259
msgid "selling price"
msgstr "prix de vente"
-#: counter/models.py:268
+#: counter/models.py:260
msgid "special selling price"
msgstr "prix de vente spécial"
-#: counter/models.py:270
+#: counter/models.py:262
msgid "icon"
msgstr "icône"
-#: counter/models.py:275
+#: counter/models.py:267
msgid "limit age"
msgstr "âge limite"
-#: counter/models.py:276
+#: counter/models.py:268
msgid "tray price"
msgstr "prix plateau"
-#: counter/models.py:280
+#: counter/models.py:272
msgid "parent product"
msgstr "produit parent"
-#: counter/models.py:286
+#: counter/models.py:278
msgid "buying groups"
msgstr "groupe d'achat"
-#: counter/models.py:288 election/models.py:52
+#: counter/models.py:280 election/models.py:52
msgid "archived"
msgstr "archivé"
-#: counter/models.py:291 counter/models.py:1006
+#: counter/models.py:283 counter/models.py:1017
msgid "product"
msgstr "produit"
-#: counter/models.py:374
+#: counter/models.py:368
msgid "products"
msgstr "produits"
-#: counter/models.py:377
+#: counter/models.py:371
msgid "counter type"
msgstr "type de comptoir"
-#: counter/models.py:379
+#: counter/models.py:373
msgid "Bar"
msgstr "Bar"
-#: counter/models.py:379
+#: counter/models.py:373
msgid "Office"
msgstr "Bureau"
-#: counter/models.py:382
+#: counter/models.py:376
msgid "sellers"
msgstr "vendeurs"
-#: counter/models.py:390 launderette/models.py:207
+#: counter/models.py:384 launderette/models.py:205
msgid "token"
msgstr "jeton"
-#: counter/models.py:618
+#: counter/models.py:619
msgid "bank"
msgstr "banque"
-#: counter/models.py:620 counter/models.py:710
+#: counter/models.py:621 counter/models.py:713
msgid "is validated"
msgstr "est validé"
-#: counter/models.py:623
+#: counter/models.py:624
msgid "refilling"
msgstr "rechargement"
-#: counter/models.py:687 eboutic/models.py:289
+#: counter/models.py:690 eboutic/models.py:280
msgid "unit price"
msgstr "prix unitaire"
-#: counter/models.py:688 counter/models.py:991 eboutic/models.py:290
+#: counter/models.py:691 counter/models.py:998 eboutic/models.py:281
msgid "quantity"
msgstr "quantité"
-#: counter/models.py:707
+#: counter/models.py:710
msgid "Sith account"
msgstr "Compte utilisateur"
-#: counter/models.py:707 sith/settings.py:385 sith/settings.py:390
-#: sith/settings.py:410
+#: counter/models.py:710 sith/settings.py:390 sith/settings.py:395
+#: sith/settings.py:415
msgid "Credit card"
msgstr "Carte bancaire"
-#: counter/models.py:713
+#: counter/models.py:716
msgid "selling"
msgstr "vente"
-#: counter/models.py:740
+#: counter/models.py:745
msgid "Unknown event"
msgstr "Événement inconnu"
-#: counter/models.py:741
+#: counter/models.py:746
#, python-format
msgid "Eticket bought for the event %(event)s"
msgstr "Eticket acheté pour l'événement %(event)s"
-#: counter/models.py:743 counter/models.py:766
+#: counter/models.py:748 counter/models.py:771
#, python-format
msgid ""
"You bought an eticket for the event %(event)s.\n"
@@ -3920,69 +3928,73 @@ msgstr ""
"Vous pouvez également retrouver tous vos e-tickets sur votre page de compte "
"%(url)s."
-#: counter/models.py:880
+#: counter/models.py:885
msgid "last activity date"
msgstr "dernière activité"
-#: counter/models.py:883
+#: counter/models.py:888
msgid "permanency"
msgstr "permanence"
-#: counter/models.py:916
+#: counter/models.py:921
msgid "emptied"
msgstr "coffre vidée"
-#: counter/models.py:919
+#: counter/models.py:924
msgid "cash register summary"
msgstr "relevé de caisse"
-#: counter/models.py:987
+#: counter/models.py:994
msgid "cash summary"
msgstr "relevé"
-#: counter/models.py:990
+#: counter/models.py:997
msgid "value"
msgstr "valeur"
-#: counter/models.py:992
+#: counter/models.py:1000
msgid "check"
msgstr "chèque"
-#: counter/models.py:995
+#: counter/models.py:1002
+msgid "True if this is a bank check, else False"
+msgstr "Vrai si c'est un chèque, sinon Faux."
+
+#: counter/models.py:1006
msgid "cash register summary item"
msgstr "élément de relevé de caisse"
-#: counter/models.py:1010
+#: counter/models.py:1021
msgid "banner"
msgstr "bannière"
-#: counter/models.py:1012
+#: counter/models.py:1023
msgid "event date"
msgstr "date de l'événement"
-#: counter/models.py:1014
+#: counter/models.py:1025
msgid "event title"
msgstr "titre de l'événement"
-#: counter/models.py:1016
+#: counter/models.py:1027
msgid "secret"
msgstr "secret"
-#: counter/models.py:1072
+#: counter/models.py:1085
msgid "uid"
msgstr "uid"
-#: counter/models.py:1077
+#: counter/models.py:1090
msgid "student cards"
msgstr "cartes étudiante"
#: counter/templates/counter/activity.jinja:5
-#: counter/templates/counter/activity.jinja:9
+#: counter/templates/counter/activity.jinja:13
#, python-format
msgid "%(counter_name)s activity"
msgstr "Activité sur %(counter_name)s"
-#: counter/templates/counter/activity.jinja:11
+#: counter/templates/counter/activity.jinja:15
msgid "Barmen list"
msgstr "Barmans"
@@ -3990,15 +4002,15 @@ msgstr "Barmans"
msgid "There is currently no barman connected."
msgstr "Il n'y a actuellement aucun barman connecté."
-#: counter/templates/counter/activity.jinja:19
+#: counter/templates/counter/activity.jinja:28
msgid "Legend"
msgstr "Légende"
-#: counter/templates/counter/activity.jinja:20
+#: counter/templates/counter/activity.jinja:32
msgid "counter is open, there's at least one barman connected"
msgstr "Le comptoir est ouvert, et il y a au moins un barman connecté"
-#: counter/templates/counter/activity.jinja:22
+#: counter/templates/counter/activity.jinja:36
#, python-format
msgid ""
"counter is open but not active, the last sale was done at least %(minutes)s "
@@ -4007,7 +4019,7 @@ msgstr ""
"Le comptoir est ouvert, mais inactif. La dernière vente a eu lieu il y a "
"%(minutes)s minutes."
-#: counter/templates/counter/activity.jinja:24
+#: counter/templates/counter/activity.jinja:40
msgid "counter is not open : no one is connected"
msgstr "Le comptoir est fermé"
@@ -4028,7 +4040,7 @@ msgstr "Liste des relevés de caisse"
msgid "Theoric sums"
msgstr "Sommes théoriques"
-#: counter/templates/counter/cash_summary_list.jinja:36 counter/views.py:1085
+#: counter/templates/counter/cash_summary_list.jinja:36 counter/views.py:1079
msgid "Emptied"
msgstr "Coffre vidé"
@@ -4054,7 +4066,7 @@ msgstr "Ce n'est pas un UID de carte étudiante valide"
#: counter/templates/counter/invoices_call.jinja:16
#: launderette/templates/launderette/launderette_admin.jinja:35
#: launderette/templates/launderette/launderette_click.jinja:13
-#: sas/templates/sas/picture.jinja:82
+#: sas/templates/sas/picture.jinja:140
#: subscription/templates/subscription/stats.jinja:19
msgid "Go"
msgstr "Valider"
@@ -4148,6 +4160,10 @@ msgstr "Merci de vous identifier"
msgid "Barman: "
msgstr "Barman : "
+#: counter/templates/counter/counter_main.jinja:56
+msgid "login"
+msgstr "login"
+
#: counter/templates/counter/eticket_list.jinja:4
#: counter/templates/counter/eticket_list.jinja:10
msgid "Eticket list"
@@ -4220,139 +4236,139 @@ msgstr "Vendeur"
msgid "%(counter_name)s stats"
msgstr "Stats sur %(counter_name)s"
-#: counter/templates/counter/stats.jinja:14
+#: counter/templates/counter/stats.jinja:15
#, python-format
msgid "Top 100 %(counter_name)s"
msgstr "Top 100 %(counter_name)s"
-#: counter/templates/counter/stats.jinja:20
-#: counter/templates/counter/stats.jinja:44
-#: counter/templates/counter/stats.jinja:66
+#: counter/templates/counter/stats.jinja:22
+#: counter/templates/counter/stats.jinja:48
+#: counter/templates/counter/stats.jinja:70
msgid "Promo"
msgstr "Promo"
-#: counter/templates/counter/stats.jinja:22
+#: counter/templates/counter/stats.jinja:24
msgid "Percentage"
msgstr "Pourcentage"
-#: counter/templates/counter/stats.jinja:38
+#: counter/templates/counter/stats.jinja:41
#, python-format
msgid "Top 100 barman %(counter_name)s"
msgstr "Top 100 barman %(counter_name)s"
-#: counter/templates/counter/stats.jinja:45
-#: counter/templates/counter/stats.jinja:67
+#: counter/templates/counter/stats.jinja:49
+#: counter/templates/counter/stats.jinja:71
msgid "Time"
msgstr "Temps"
-#: counter/templates/counter/stats.jinja:60
+#: counter/templates/counter/stats.jinja:64
#, python-format
msgid "Top 100 barman %(counter_name)s (all semesters)"
msgstr "Top 100 barman %(counter_name)s (tous les semestres)"
-#: counter/views.py:177
+#: counter/views.py:170
msgid "Cash summary"
msgstr "Relevé de caisse"
-#: counter/views.py:191
+#: counter/views.py:186
msgid "Last operations"
msgstr "Dernières opérations"
-#: counter/views.py:206
+#: counter/views.py:203
msgid "Take items from stock"
msgstr "Prendre des éléments du stock"
-#: counter/views.py:259
+#: counter/views.py:256
msgid "Bad credentials"
msgstr "Mauvais identifiants"
-#: counter/views.py:261
+#: counter/views.py:258
msgid "User is not barman"
msgstr "L'utilisateur n'est pas barman."
-#: counter/views.py:266
+#: counter/views.py:263
msgid "Bad location, someone is already logged in somewhere else"
msgstr "Mauvais comptoir, quelqu'un est déjà connecté ailleurs"
-#: counter/views.py:310
+#: counter/views.py:307
msgid "Too young for that product"
msgstr "Trop jeune pour ce produit"
-#: counter/views.py:313
+#: counter/views.py:310
msgid "Not allowed for that product"
msgstr "Non autorisé pour ce produit"
-#: counter/views.py:316
+#: counter/views.py:313
msgid "No date of birth provided"
msgstr "Pas de date de naissance renseignée"
-#: counter/views.py:619
+#: counter/views.py:613
msgid "You have not enough money to buy all the basket"
msgstr "Vous n'avez pas assez d'argent pour acheter le panier"
-#: counter/views.py:771
+#: counter/views.py:765
msgid "Counter administration"
msgstr "Administration des comptoirs"
-#: counter/views.py:773
+#: counter/views.py:767
msgid "Stocks"
msgstr "Stocks"
-#: counter/views.py:792
+#: counter/views.py:786
msgid "Product types"
msgstr "Types de produit"
-#: counter/views.py:1042
+#: counter/views.py:1036
msgid "10 cents"
msgstr "10 centimes"
-#: counter/views.py:1043
+#: counter/views.py:1037
msgid "20 cents"
msgstr "20 centimes"
-#: counter/views.py:1044
+#: counter/views.py:1038
msgid "50 cents"
msgstr "50 centimes"
-#: counter/views.py:1045
+#: counter/views.py:1039
msgid "1 euro"
msgstr "1 €"
-#: counter/views.py:1046
+#: counter/views.py:1040
msgid "2 euros"
msgstr "2 €"
-#: counter/views.py:1047
+#: counter/views.py:1041
msgid "5 euros"
msgstr "5 €"
-#: counter/views.py:1048
+#: counter/views.py:1042
msgid "10 euros"
msgstr "10 €"
-#: counter/views.py:1049
+#: counter/views.py:1043
msgid "20 euros"
msgstr "20 €"
-#: counter/views.py:1050
+#: counter/views.py:1044
msgid "50 euros"
msgstr "50 €"
-#: counter/views.py:1052
+#: counter/views.py:1046
msgid "100 euros"
msgstr "100 €"
-#: counter/views.py:1055 counter/views.py:1061 counter/views.py:1067
-#: counter/views.py:1073 counter/views.py:1079
+#: counter/views.py:1049 counter/views.py:1055 counter/views.py:1061
+#: counter/views.py:1067 counter/views.py:1073
msgid "Check amount"
msgstr "Montant du chèque"
-#: counter/views.py:1058 counter/views.py:1064 counter/views.py:1070
-#: counter/views.py:1076 counter/views.py:1082
+#: counter/views.py:1052 counter/views.py:1058 counter/views.py:1064
+#: counter/views.py:1070 counter/views.py:1076
msgid "Check quantity"
msgstr "Nombre de chèque"
-#: counter/views.py:1637
+#: counter/views.py:1632
msgid "people(s)"
msgstr "personne(s)"
@@ -4387,27 +4403,27 @@ msgstr "%(name)s : ce produit n'existe pas ou n'est peut-être plus disponible."
msgid "You cannot buy %(nbr)d %(name)s."
msgstr "Vous ne pouvez pas acheter %(nbr)d %(name)s."
-#: eboutic/models.py:237
+#: eboutic/models.py:228
msgid "validated"
msgstr "validé"
-#: eboutic/models.py:247
+#: eboutic/models.py:238
msgid "Invoice already validated"
msgstr "Facture déjà validée"
-#: eboutic/models.py:286
+#: eboutic/models.py:277
msgid "product id"
msgstr "ID du produit"
-#: eboutic/models.py:287
+#: eboutic/models.py:278
msgid "product name"
msgstr "nom du produit"
-#: eboutic/models.py:288
+#: eboutic/models.py:279
msgid "product type id"
msgstr "id du type du produit"
-#: eboutic/models.py:305
+#: eboutic/models.py:296
msgid "basket"
msgstr "panier"
@@ -4508,27 +4524,27 @@ msgstr "début des candidatures"
msgid "end candidature"
msgstr "fin des candidatures"
-#: election/models.py:38 election/views.py:161
+#: election/models.py:38 election/views.py:156
msgid "vote groups"
msgstr "groupe de vote"
-#: election/models.py:45 election/views.py:168
+#: election/models.py:45 election/views.py:163
msgid "candidature groups"
msgstr "groupe de candidature"
-#: election/models.py:116 election/models.py:163
+#: election/models.py:115 election/models.py:162
msgid "election"
msgstr "élection"
-#: election/models.py:121
+#: election/models.py:120
msgid "max choice"
msgstr "nombre de choix maxi"
-#: election/models.py:201
+#: election/models.py:200
msgid "election list"
msgstr "liste électorale"
-#: election/models.py:226
+#: election/models.py:225
msgid "candidature"
msgstr "candidature"
@@ -4576,7 +4592,7 @@ msgstr "Vous avez déjà soumis votre vote."
msgid "You have voted in this election."
msgstr "Vous avez déjà voté pour cette élection."
-#: election/templates/election/election_detail.jinja:49 election/views.py:94
+#: election/templates/election/election_detail.jinja:49 election/views.py:89
msgid "Blank vote"
msgstr "Vote blanc"
@@ -4648,23 +4664,23 @@ msgstr "au"
msgid "Polls open from"
msgstr "Votes ouverts du"
-#: election/views.py:45
+#: election/views.py:40
msgid "You have selected too much candidates."
msgstr "Vous avez sélectionné trop de candidats."
-#: election/views.py:61
+#: election/views.py:56
msgid "User to candidate"
msgstr "Utilisateur se présentant"
-#: election/views.py:119
+#: election/views.py:114
msgid "This role already exists for this election"
msgstr "Ce rôle existe déjà pour cette élection"
-#: election/views.py:174
+#: election/views.py:169
msgid "Start candidature"
msgstr "Début des candidatures"
-#: election/views.py:176
+#: election/views.py:171
msgid "End candidature"
msgstr "Fin des candidatures"
@@ -4680,7 +4696,7 @@ msgstr "club propriétaire"
msgid "number to choose a specific forum ordering"
msgstr "numéro spécifiant l'ordre d'affichage"
-#: forum/models.py:93 forum/models.py:247
+#: forum/models.py:93 forum/models.py:250
msgid "the last message"
msgstr "le dernier message"
@@ -4688,47 +4704,47 @@ msgstr "le dernier message"
msgid "number of topics"
msgstr "nombre de sujets"
-#: forum/models.py:184
+#: forum/models.py:187
msgid "You can not make loops in forums"
msgstr "Vous ne pouvez pas faire de boucles dans les forums"
-#: forum/models.py:242
+#: forum/models.py:245
msgid "subscribed users"
msgstr "utilisateurs abonnés"
-#: forum/models.py:252
+#: forum/models.py:255
msgid "number of messages"
msgstr "nombre de messages"
-#: forum/models.py:310
+#: forum/models.py:313
msgid "message"
msgstr "message"
-#: forum/models.py:313
+#: forum/models.py:316
msgid "readers"
msgstr "lecteurs"
-#: forum/models.py:315
+#: forum/models.py:318
msgid "is deleted"
msgstr "est supprimé"
-#: forum/models.py:395
+#: forum/models.py:401
msgid "Message edited by"
msgstr "Message édité par"
-#: forum/models.py:396
+#: forum/models.py:402
msgid "Message deleted by"
msgstr "Message supprimé par"
-#: forum/models.py:397
+#: forum/models.py:403
msgid "Message undeleted by"
msgstr "Message restauré par"
-#: forum/models.py:409
+#: forum/models.py:415
msgid "action"
msgstr "action"
-#: forum/models.py:428
+#: forum/models.py:434
msgid "last read date"
msgstr "dernière date de lecture"
@@ -4739,25 +4755,25 @@ msgstr "dernière date de lecture"
msgid "Favorite topics"
msgstr "Topics favoris"
-#: forum/templates/forum/forum.jinja:14 forum/templates/forum/main.jinja:22
+#: forum/templates/forum/forum.jinja:18 forum/templates/forum/main.jinja:25
msgid "New forum"
msgstr "Nouveau forum"
-#: forum/templates/forum/forum.jinja:17 forum/templates/forum/reply.jinja:8
+#: forum/templates/forum/forum.jinja:21 forum/templates/forum/reply.jinja:8
#: forum/templates/forum/reply.jinja:28
msgid "New topic"
msgstr "Nouveau sujet"
-#: forum/templates/forum/forum.jinja:28 forum/templates/forum/main.jinja:31
+#: forum/templates/forum/forum.jinja:32 forum/templates/forum/main.jinja:34
msgid "Topics"
msgstr "Sujets"
-#: forum/templates/forum/forum.jinja:31 forum/templates/forum/forum.jinja:53
-#: forum/templates/forum/main.jinja:34
+#: forum/templates/forum/forum.jinja:35 forum/templates/forum/forum.jinja:57
+#: forum/templates/forum/main.jinja:37
msgid "Last message"
msgstr "Dernier message"
-#: forum/templates/forum/forum.jinja:50
+#: forum/templates/forum/forum.jinja:54
msgid "Messages"
msgstr "Messages"
@@ -4812,51 +4828,59 @@ msgstr "Enlever des favoris"
msgid "Mark as favorite"
msgstr "Ajouter aux favoris"
-#: forum/views.py:190
+#: forum/views.py:189
msgid "Apply rights and club owner recursively"
msgstr "Appliquer les droits et le club propriétaire récursivement"
-#: forum/views.py:410
+#: forum/views.py:409
#, python-format
msgid "%(author)s said"
msgstr "Citation de %(author)s"
-#: galaxy/models.py:51
+#: galaxy/models.py:57
msgid "star owner"
msgstr "propriétaire de l'étoile"
-#: galaxy/models.py:56
+#: galaxy/models.py:62
msgid "star mass"
msgstr "masse de l'étoile"
-#: galaxy/models.py:73
+#: galaxy/models.py:67
+msgid "the galaxy this star belongs to"
+msgstr "la galaxie à laquelle cette étoile appartient"
+
+#: galaxy/models.py:103
msgid "galaxy star 1"
msgstr "étoile 1"
-#: galaxy/models.py:79
+#: galaxy/models.py:109
msgid "galaxy star 2"
msgstr "étoile 2"
-#: galaxy/models.py:84
+#: galaxy/models.py:114
msgid "distance"
msgstr "distance"
-#: galaxy/models.py:86
+#: galaxy/models.py:116
msgid "Distance separating star1 and star2"
msgstr "Distance séparant étoile 1 et étoile 2"
-#: galaxy/models.py:89
+#: galaxy/models.py:119
msgid "family score"
msgstr "score de famille"
-#: galaxy/models.py:93
+#: galaxy/models.py:123
msgid "pictures score"
msgstr "score de photos"
-#: galaxy/models.py:97
+#: galaxy/models.py:127
msgid "clubs score"
msgstr "score de club"
+#: galaxy/models.py:179
+msgid "The galaxy current state"
+msgstr "L'état actuel de la galaxie"
+
#: galaxy/templates/galaxy/user.jinja:4
#, python-format
msgid "%(user_name)s's Galaxy"
@@ -4866,31 +4890,31 @@ msgstr "Galaxie de %(user_name)s"
msgid "This citizen has not yet joined the galaxy"
msgstr "Ce citoyen n'a pas encore rejoint la galaxie"
-#: launderette/models.py:97 launderette/models.py:135
+#: launderette/models.py:91 launderette/models.py:131
msgid "launderette"
msgstr "laverie"
-#: launderette/models.py:103
+#: launderette/models.py:97
msgid "is working"
msgstr "fonctionne"
-#: launderette/models.py:106
+#: launderette/models.py:100
msgid "Machine"
msgstr "Machine"
-#: launderette/models.py:141
+#: launderette/models.py:137
msgid "borrow date"
msgstr "date d'emprunt"
-#: launderette/models.py:152
+#: launderette/models.py:148
msgid "Token"
msgstr "Jeton"
-#: launderette/models.py:158
+#: launderette/models.py:154
msgid "Token name can not be blank"
msgstr "Le nom du jeton ne peut pas être vide"
-#: launderette/models.py:201
+#: launderette/models.py:199
msgid "machine"
msgstr "machine"
@@ -4919,12 +4943,12 @@ msgid "Washing and drying"
msgstr "Lavage et séchage"
#: launderette/templates/launderette/launderette_book.jinja:27
-#: sith/settings.py:622
+#: sith/settings.py:626
msgid "Washing"
msgstr "Lavage"
#: launderette/templates/launderette/launderette_book.jinja:31
-#: sith/settings.py:622
+#: sith/settings.py:626
msgid "Drying"
msgstr "Séchage"
@@ -4949,25 +4973,25 @@ msgstr "Éditer la page de présentation"
msgid "Book launderette slot"
msgstr "Réserver un créneau de laverie"
-#: launderette/views.py:241
+#: launderette/views.py:232
msgid "Tokens, separated by spaces"
msgstr "Jetons, séparés par des espaces"
-#: launderette/views.py:261 launderette/views.py:283
+#: launderette/views.py:252 launderette/views.py:274
#, python-format
msgid "Token %(token_name)s does not exists"
msgstr "Le jeton %(token_name)s n'existe pas"
-#: launderette/views.py:272
+#: launderette/views.py:263
#, python-format
msgid "Token %(token_name)s already exists"
msgstr "Un jeton %(token_name)s existe déjà"
-#: launderette/views.py:339
+#: launderette/views.py:330
msgid "User has booked no slot"
msgstr "L'utilisateur n'a pas réservé de créneau"
-#: launderette/views.py:451
+#: launderette/views.py:442
msgid "Token not found"
msgstr "Jeton non trouvé"
@@ -4992,27 +5016,27 @@ msgstr "Recherche inversée"
msgid "Quick search"
msgstr "Recherche rapide"
-#: matmat/views.py:72
+#: matmat/views.py:71
msgid "Last/First name or nickname"
msgstr "Nom de famille, prénom ou surnom"
-#: pedagogy/forms.py:87
+#: pedagogy/forms.py:84
msgid "Do not vote"
msgstr "Ne pas voter"
-#: pedagogy/forms.py:136
+#: pedagogy/forms.py:133
msgid "This user has already commented on this UV"
msgstr "Cet utilisateur a déjà commenté cette UV"
-#: pedagogy/forms.py:172
+#: pedagogy/forms.py:169
msgid "Accepted reports"
msgstr "Signalements acceptés"
-#: pedagogy/forms.py:179
+#: pedagogy/forms.py:176
msgid "Denied reports"
msgstr "Signalements refusés"
-#: pedagogy/models.py:53
+#: pedagogy/models.py:52
msgid ""
"The code of an UV must only contains uppercase characters without accent and "
"numbers"
@@ -5020,103 +5044,103 @@ msgstr ""
"Le code d'une UV doit seulement contenir des caractères majuscule sans "
"accents et nombres"
-#: pedagogy/models.py:67
+#: pedagogy/models.py:66
msgid "credit type"
msgstr "type de crédit"
-#: pedagogy/models.py:72 pedagogy/models.py:102
+#: pedagogy/models.py:71 pedagogy/models.py:101
msgid "uv manager"
msgstr "gestionnaire d'uv"
-#: pedagogy/models.py:80
+#: pedagogy/models.py:79
msgid "language"
msgstr "langue"
-#: pedagogy/models.py:86
+#: pedagogy/models.py:85
msgid "credits"
msgstr "crédits"
-#: pedagogy/models.py:94
+#: pedagogy/models.py:93
msgid "departmenmt"
msgstr "département"
-#: pedagogy/models.py:103
+#: pedagogy/models.py:102
msgid "objectives"
msgstr "objectifs"
-#: pedagogy/models.py:104
+#: pedagogy/models.py:103
msgid "program"
msgstr "programme"
-#: pedagogy/models.py:105
+#: pedagogy/models.py:104
msgid "skills"
msgstr "compétences"
-#: pedagogy/models.py:106
+#: pedagogy/models.py:105
msgid "key concepts"
msgstr "concepts clefs"
-#: pedagogy/models.py:111
+#: pedagogy/models.py:110
msgid "hours CM"
msgstr "heures CM"
-#: pedagogy/models.py:118
+#: pedagogy/models.py:117
msgid "hours TD"
msgstr "heures TD"
-#: pedagogy/models.py:125
+#: pedagogy/models.py:124
msgid "hours TP"
msgstr "heures TP"
-#: pedagogy/models.py:132
+#: pedagogy/models.py:131
msgid "hours THE"
msgstr "heures THE"
-#: pedagogy/models.py:139
+#: pedagogy/models.py:138
msgid "hours TE"
msgstr "heures TE"
-#: pedagogy/models.py:217 pedagogy/models.py:291
+#: pedagogy/models.py:216 pedagogy/models.py:290
msgid "uv"
msgstr "UE"
-#: pedagogy/models.py:221
+#: pedagogy/models.py:220
msgid "global grade"
msgstr "note globale"
-#: pedagogy/models.py:228
+#: pedagogy/models.py:227
msgid "utility grade"
msgstr "note d'utilité"
-#: pedagogy/models.py:235
+#: pedagogy/models.py:234
msgid "interest grade"
msgstr "note d'intérêt"
-#: pedagogy/models.py:242
+#: pedagogy/models.py:241
msgid "teaching grade"
msgstr "note d'enseignement"
-#: pedagogy/models.py:249
+#: pedagogy/models.py:248
msgid "work load grade"
msgstr "note de charge de travail"
-#: pedagogy/models.py:255
+#: pedagogy/models.py:254
msgid "publish date"
msgstr "date de publication"
-#: pedagogy/models.py:297
+#: pedagogy/models.py:296
msgid "grade"
msgstr "note"
-#: pedagogy/models.py:317
+#: pedagogy/models.py:316
msgid "report"
msgstr "signaler"
-#: pedagogy/models.py:323
+#: pedagogy/models.py:322
msgid "reporter"
msgstr "signalant"
-#: pedagogy/models.py:326
+#: pedagogy/models.py:325
msgid "reason"
msgstr "raison"
@@ -5240,7 +5264,7 @@ msgstr "Concepts clefs"
msgid "UE manager: "
msgstr "Gestionnaire d'UE : "
-#: pedagogy/templates/pedagogy/uv_detail.jinja:86 pedagogy/tests.py:453
+#: pedagogy/templates/pedagogy/uv_detail.jinja:86 pedagogy/tests.py:405
msgid ""
"You already posted a comment on this UV. If you want to comment again, "
"please modify or delete your previous comment."
@@ -5253,7 +5277,7 @@ msgid "Leave comment"
msgstr "Laisser un commentaire"
#: pedagogy/templates/pedagogy/uv_detail.jinja:146
-#: stock/templates/stock/shopping_list_items.jinja:42 stock/views.py:278
+#: stock/templates/stock/shopping_list_items.jinja:42 stock/views.py:263
#: trombi/templates/trombi/export.jinja:70
msgid "Comments"
msgstr "Commentaires"
@@ -5328,42 +5352,40 @@ msgstr "Utilisateur qui sera supprimé"
msgid "User to be selected"
msgstr "Utilisateur à sélectionner"
-#: sas/models.py:252
+#: sas/models.py:248
msgid "picture"
msgstr "photo"
-#: sas/templates/sas/album.jinja:5 sas/templates/sas/main.jinja:4
-#: sas/templates/sas/main.jinja:32 sas/templates/sas/picture.jinja:34
+#: sas/templates/sas/album.jinja:9 sas/templates/sas/main.jinja:8
+#: sas/templates/sas/main.jinja:39 sas/templates/sas/picture.jinja:20
msgid "SAS"
msgstr "SAS"
-#: sas/templates/sas/album.jinja:102
+#: sas/templates/sas/album.jinja:57 sas/templates/sas/moderation.jinja:10
+msgid "Albums"
+msgstr "Albums"
+
+#: sas/templates/sas/album.jinja:109
msgid "This album does not contain any photos."
msgstr "Cet album ne contient aucune photo."
-#: sas/templates/sas/album.jinja:53 sas/templates/sas/album.jinja:55
-#: sas/templates/sas/main.jinja:13 sas/templates/sas/main.jinja:15
-#: sas/templates/sas/main.jinja:17
-msgid "preview"
-msgstr "miniature"
+#: sas/templates/sas/album.jinja:128
+msgid "Upload"
+msgstr "Envoyer"
+
+#: sas/templates/sas/album.jinja:135
+msgid "Template generation time: "
+msgstr "Temps de génération du template : "
#: sas/templates/sas/main.jinja:42
msgid "You must be logged in to see the SAS."
msgstr "Vous devez être connecté pour voir les photos."
-#: sas/templates/sas/album.jinja:89
-msgid "Upload"
-msgstr "Envoyer"
-
-#: sas/templates/sas/album.jinja:91
-msgid "Template generation time: "
-msgstr "Temps de génération du template : "
-
-#: sas/templates/sas/main.jinja:34
+#: sas/templates/sas/main.jinja:45
msgid "Latest albums"
msgstr "Derniers albums"
-#: sas/templates/sas/main.jinja:41
+#: sas/templates/sas/main.jinja:60 sas/templates/sas/main.jinja:79
msgid "All categories"
msgstr "Toutes les catégories"
@@ -5371,455 +5393,437 @@ msgstr "Toutes les catégories"
msgid "SAS moderation"
msgstr "Modération du SAS"
-#: sas/templates/sas/moderation.jinja:10
-msgid "Albums"
-msgstr "Albums"
-
-#: sas/templates/sas/picture.jinja:68
-msgid "People"
-msgstr "Personne(s)"
-
-#: sas/templates/sas/picture.jinja:97
-msgid "HD version"
-msgstr "Version HD"
-
-#: sas/templates/sas/picture.jinja:101
-msgid "Rotate left"
-msgstr "Tourner vers la gauche"
-
-#: sas/templates/sas/picture.jinja:102
-msgid "Rotate right"
-msgstr "Tourner vers la droite"
-
-#: sas/templates/sas/picture.jinja:103
-msgid "Ask for removal"
-msgstr "Demander le retrait"
-
-#: sas/templates/sas/picture.jinja:119
+#: sas/templates/sas/picture.jinja:54
msgid "Asked for removal"
msgstr "Retrait demandé"
-#: sas/views.py:49
+#: sas/templates/sas/picture.jinja:103
+msgid "HD version"
+msgstr "Version HD"
+
+#: sas/templates/sas/picture.jinja:105
+msgid "Ask for removal"
+msgstr "Demander le retrait"
+
+#: sas/templates/sas/picture.jinja:136
+msgid "People"
+msgstr "Personne(s)"
+
+#: sas/views.py:39
msgid "Add a new album"
msgstr "Ajouter un nouvel album"
-#: sas/views.py:53
+#: sas/views.py:42
msgid "Upload images"
msgstr "Envoyer les images"
-#: sas/views.py:71
+#: sas/views.py:60
#, python-format
msgid "Error creating album %(album)s: %(msg)s"
msgstr "Erreur de création de l'album %(album)s : %(msg)s"
-#: sas/views.py:106 trombi/templates/trombi/detail.jinja:15
+#: sas/views.py:95 trombi/templates/trombi/detail.jinja:15
msgid "Add user"
msgstr "Ajouter une personne"
-#: sith/settings.py:244 sith/settings.py:447
+#: sith/settings.py:246 sith/settings.py:452
msgid "English"
msgstr "Anglais"
-#: sith/settings.py:244 sith/settings.py:446
+#: sith/settings.py:246 sith/settings.py:451
msgid "French"
msgstr "Français"
-#: sith/settings.py:366
+#: sith/settings.py:371
msgid "TC"
msgstr "TC"
-#: sith/settings.py:367
+#: sith/settings.py:372
msgid "IMSI"
msgstr "IMSI"
-#: sith/settings.py:368
+#: sith/settings.py:373
msgid "IMAP"
msgstr "IMAP"
-#: sith/settings.py:369
+#: sith/settings.py:374
msgid "INFO"
msgstr "INFO"
-#: sith/settings.py:370
+#: sith/settings.py:375
msgid "GI"
msgstr "GI"
-#: sith/settings.py:371 sith/settings.py:457
+#: sith/settings.py:376 sith/settings.py:462
msgid "E"
msgstr "E"
-#: sith/settings.py:372
+#: sith/settings.py:377
msgid "EE"
msgstr "EE"
-#: sith/settings.py:373
+#: sith/settings.py:378
msgid "GESC"
msgstr "GESC"
-#: sith/settings.py:374
+#: sith/settings.py:379
msgid "GMC"
msgstr "GMC"
-#: sith/settings.py:375
+#: sith/settings.py:380
msgid "MC"
msgstr "MC"
-#: sith/settings.py:376
+#: sith/settings.py:381
msgid "EDIM"
msgstr "EDIM"
-#: sith/settings.py:377
+#: sith/settings.py:382
msgid "Humanities"
msgstr "Humanités"
-#: sith/settings.py:378
+#: sith/settings.py:383
msgid "N/A"
msgstr "N/A"
-#: sith/settings.py:382 sith/settings.py:389 sith/settings.py:408
+#: sith/settings.py:387 sith/settings.py:394 sith/settings.py:413
msgid "Check"
msgstr "Chèque"
-#: sith/settings.py:383 sith/settings.py:391 sith/settings.py:409
+#: sith/settings.py:388 sith/settings.py:396 sith/settings.py:414
msgid "Cash"
msgstr "Espèces"
-#: sith/settings.py:384
+#: sith/settings.py:389
msgid "Transfert"
msgstr "Virement"
-#: sith/settings.py:397
+#: sith/settings.py:402
msgid "Belfort"
msgstr "Belfort"
-#: sith/settings.py:398
+#: sith/settings.py:403
msgid "Sevenans"
msgstr "Sevenans"
-#: sith/settings.py:399
+#: sith/settings.py:404
msgid "Montbéliard"
msgstr "Montbéliard"
-#: sith/settings.py:427
+#: sith/settings.py:432
msgid "Free"
msgstr "Libre"
-#: sith/settings.py:428
+#: sith/settings.py:433
msgid "CS"
msgstr "CS"
-#: sith/settings.py:429
+#: sith/settings.py:434
msgid "TM"
msgstr "TM"
-#: sith/settings.py:430
+#: sith/settings.py:435
msgid "OM"
msgstr "OM"
-#: sith/settings.py:431
+#: sith/settings.py:436
msgid "QC"
msgstr "QC"
-#: sith/settings.py:432
+#: sith/settings.py:437
msgid "EC"
msgstr "EC"
-#: sith/settings.py:433
+#: sith/settings.py:438
msgid "RN"
msgstr "RN"
-#: sith/settings.py:434
+#: sith/settings.py:439
msgid "ST"
msgstr "ST"
-#: sith/settings.py:435
+#: sith/settings.py:440
msgid "EXT"
msgstr "EXT"
-#: sith/settings.py:440
+#: sith/settings.py:445
msgid "Autumn"
msgstr "Automne"
-#: sith/settings.py:441
+#: sith/settings.py:446
msgid "Spring"
msgstr "Printemps"
-#: sith/settings.py:442
+#: sith/settings.py:447
msgid "Autumn and spring"
msgstr "Automne et printemps"
-#: sith/settings.py:448
+#: sith/settings.py:453
msgid "German"
msgstr "Allemand"
-#: sith/settings.py:449
+#: sith/settings.py:454
msgid "Spanish"
msgstr "Espagnol"
-#: sith/settings.py:453
+#: sith/settings.py:458
msgid "A"
msgstr "A"
-#: sith/settings.py:454
+#: sith/settings.py:459
msgid "B"
msgstr "B"
-#: sith/settings.py:455
+#: sith/settings.py:460
msgid "C"
msgstr "C"
-#: sith/settings.py:456
+#: sith/settings.py:461
msgid "D"
msgstr "D"
-#: sith/settings.py:458
+#: sith/settings.py:463
msgid "FX"
msgstr "FX"
-#: sith/settings.py:459
+#: sith/settings.py:464
msgid "F"
msgstr "F"
-#: sith/settings.py:460
+#: sith/settings.py:465
msgid "Abs"
msgstr "Abs"
-#: sith/settings.py:464
+#: sith/settings.py:469
msgid "Selling deletion"
msgstr "Suppression de vente"
-#: sith/settings.py:465
+#: sith/settings.py:470
msgid "Refilling deletion"
msgstr "Suppression de rechargement"
-#: sith/settings.py:502
+#: sith/settings.py:507
msgid "One semester"
msgstr "Un semestre, 20 €"
-#: sith/settings.py:503
+#: sith/settings.py:508
msgid "Two semesters"
msgstr "Deux semestres, 35 €"
-#: sith/settings.py:505
+#: sith/settings.py:510
msgid "Common core cursus"
msgstr "Cursus tronc commun, 60 €"
-#: sith/settings.py:509
+#: sith/settings.py:514
msgid "Branch cursus"
msgstr "Cursus branche, 60 €"
-#: sith/settings.py:510
+#: sith/settings.py:515
msgid "Alternating cursus"
msgstr "Cursus alternant, 30 €"
-#: sith/settings.py:511
+#: sith/settings.py:516
msgid "Honorary member"
msgstr "Membre honoraire, 0 €"
-#: sith/settings.py:512
+#: sith/settings.py:517
msgid "Assidu member"
msgstr "Membre d'Assidu, 0 €"
-#: sith/settings.py:513
+#: sith/settings.py:518
msgid "Amicale/DOCEO member"
msgstr "Membre de l'Amicale/DOCEO, 0 €"
-#: sith/settings.py:514
+#: sith/settings.py:519
msgid "UT network member"
msgstr "Cotisant du réseau UT, 0 €"
-#: sith/settings.py:515
+#: sith/settings.py:520
msgid "CROUS member"
msgstr "Membres du CROUS, 0 €"
-#: sith/settings.py:516
+#: sith/settings.py:521
msgid "Sbarro/ESTA member"
msgstr "Membre de Sbarro ou de l'ESTA, 20 €"
-#: sith/settings.py:518
+#: sith/settings.py:523
msgid "One semester Welcome Week"
msgstr "Un semestre Welcome Week"
-#: sith/settings.py:522
+#: sith/settings.py:527
msgid "One month for free"
msgstr "Un mois gratuit"
-#: sith/settings.py:523
+#: sith/settings.py:528
msgid "Two months for free"
msgstr "Deux mois gratuits"
-#: sith/settings.py:524
+#: sith/settings.py:529
msgid "Eurok's volunteer"
msgstr "Bénévole Eurockéennes"
-#: sith/settings.py:526
+#: sith/settings.py:531
msgid "Six weeks for free"
msgstr "6 semaines gratuites"
-#: sith/settings.py:530
+#: sith/settings.py:535
msgid "One day"
msgstr "Un jour"
-#: sith/settings.py:531
+#: sith/settings.py:536
msgid "GA staff member"
msgstr "Membre staff GA (2 semaines), 1 €"
-#: sith/settings.py:534
+#: sith/settings.py:539
msgid "One semester (-20%)"
msgstr "Un semestre (-20%), 12 €"
-#: sith/settings.py:539
+#: sith/settings.py:544
msgid "Two semesters (-20%)"
msgstr "Deux semestres (-20%), 22 €"
-#: sith/settings.py:544
+#: sith/settings.py:549
msgid "Common core cursus (-20%)"
msgstr "Cursus tronc commun (-20%), 36 €"
-#: sith/settings.py:549
+#: sith/settings.py:554
msgid "Branch cursus (-20%)"
msgstr "Cursus branche (-20%), 36 €"
-#: sith/settings.py:554
+#: sith/settings.py:559
msgid "Alternating cursus (-20%)"
msgstr "Cursus alternant (-20%), 24 €"
-#: sith/settings.py:560
+#: sith/settings.py:565
msgid "One year for free(CA offer)"
msgstr "Une année offerte (Offre CA)"
-#: sith/settings.py:582
+#: sith/settings.py:585
msgid "President"
msgstr "Président⸱e"
-#: sith/settings.py:583
+#: sith/settings.py:586
msgid "Vice-President"
msgstr "Vice-Président⸱e"
-#: sith/settings.py:584
+#: sith/settings.py:587
msgid "Treasurer"
msgstr "Trésorier⸱e"
-#: sith/settings.py:585
+#: sith/settings.py:588
msgid "Communication supervisor"
msgstr "Responsable communication"
-#: sith/settings.py:586
+#: sith/settings.py:589
msgid "Secretary"
msgstr "Secrétaire"
-#: sith/settings.py:587
+#: sith/settings.py:590
msgid "IT supervisor"
msgstr "Responsable info"
-#: sith/settings.py:588
+#: sith/settings.py:591
msgid "Board member"
msgstr "Membre du bureau"
-#: sith/settings.py:589
+#: sith/settings.py:592
msgid "Active member"
msgstr "Membre actif⸱ve"
-#: sith/settings.py:590
+#: sith/settings.py:593
msgid "Curious"
msgstr "Curieux⸱euse"
-#: sith/settings.py:626
+#: sith/settings.py:630
msgid "A new poster needs to be moderated"
msgstr "Une nouvelle affiche a besoin d'être modérée"
-#: sith/settings.py:627
+#: sith/settings.py:631
msgid "A new mailing list needs to be moderated"
msgstr "Une nouvelle mailing list a besoin d'être modérée"
-#: sith/settings.py:630
+#: sith/settings.py:634
msgid "A new pedagogy comment has been signaled for moderation"
msgstr ""
"Un nouveau commentaire de la pédagogie a été signalé pour la modération"
-#: sith/settings.py:632
+#: sith/settings.py:636
#, python-format
msgid "There are %s fresh news to be moderated"
msgstr "Il y a %s nouvelles toutes fraîches à modérer"
-#: sith/settings.py:633
+#: sith/settings.py:637
msgid "New files to be moderated"
msgstr "Nouveaux fichiers à modérer"
-#: sith/settings.py:634
+#: sith/settings.py:638
#, python-format
msgid "There are %s pictures to be moderated in the SAS"
msgstr "Il y a %s photos à modérer dans le SAS"
-#: sith/settings.py:635
+#: sith/settings.py:639
msgid "You've been identified on some pictures"
msgstr "Vous avez été identifié sur des photos"
-#: sith/settings.py:636
+#: sith/settings.py:640
#, python-format
msgid "You just refilled of %s €"
msgstr "Vous avez rechargé votre compte de %s€"
-#: sith/settings.py:637
+#: sith/settings.py:641
#, python-format
msgid "You just bought %s"
msgstr "Vous avez acheté %s"
-#: sith/settings.py:638
+#: sith/settings.py:642
msgid "You have a notification"
msgstr "Vous avez une notification"
-#: core/templates/core/base.jinja
-msgid "You do not have any unread notification"
-msgstr "Vous n'avez aucune notification non lue"
-
-#: sith/settings.py:624
-#: sith/settings.py:648
-#: sith/settings.py:650
+#: sith/settings.py:654
msgid "Success!"
msgstr "Succès !"
-#: sith/settings.py:651
+#: sith/settings.py:655
msgid "Fail!"
msgstr "Échec !"
-#: sith/settings.py:652
+#: sith/settings.py:656
msgid "You successfully posted an article in the Weekmail"
msgstr "Article posté avec succès dans le Weekmail"
-#: sith/settings.py:653
+#: sith/settings.py:657
msgid "You successfully edited an article in the Weekmail"
msgstr "Article édité avec succès dans le Weekmail"
-#: sith/settings.py:654
+#: sith/settings.py:658
msgid "You successfully sent the Weekmail"
msgstr "Weekmail envoyé avec succès"
-#: sith/settings.py:662
+#: sith/settings.py:666
msgid "AE tee-shirt"
msgstr "Tee-shirt AE"
-#: stock/models.py:65
+#: stock/models.py:64
msgid "unit quantity"
msgstr "quantité unitaire"
-#: stock/models.py:65
+#: stock/models.py:64
msgid "number of element in one box"
msgstr "nombre d'éléments par boîte"
-#: stock/models.py:68
+#: stock/models.py:67
msgid "effective quantity"
msgstr "quantité effective"
-#: stock/models.py:68
+#: stock/models.py:67
msgid "number of box"
msgstr "nombre de boîtes"
-#: stock/models.py:71
+#: stock/models.py:70
msgid "minimal quantity"
msgstr "quantité minimale"
-#: stock/models.py:74
+#: stock/models.py:73
msgid ""
"if the effective quantity is less than the minimal, item is added to the "
"shopping list"
@@ -5827,27 +5831,27 @@ msgstr ""
"si la quantité effective est en dessous du minima, l'item est ajouté àla "
"liste de courses"
-#: stock/models.py:106
+#: stock/models.py:105
msgid "todo"
msgstr "à faire"
-#: stock/models.py:127
+#: stock/models.py:126
msgid "shopping lists"
msgstr "listes de courses"
-#: stock/models.py:143
+#: stock/models.py:142
msgid "quantity to buy"
msgstr "quantité à acheter"
-#: stock/models.py:145
+#: stock/models.py:144
msgid "quantity to buy during the next shopping session"
msgstr "quantité à acheter pendant les prochaines courses"
-#: stock/models.py:148
+#: stock/models.py:147
msgid "quantity bought"
msgstr "quantité achetée"
-#: stock/models.py:150
+#: stock/models.py:149
msgid "quantity bought during the last shopping session"
msgstr "quantité achetée pendant les dernières courses"
@@ -5968,15 +5972,15 @@ msgstr "Mettre à jour les quantités de %(s)s après les courses"
msgid "Update stock quantities"
msgstr "Mettre à jour les quantités en stock"
-#: stock/views.py:257
+#: stock/views.py:242
msgid "Shopping list name"
msgstr "Nom de la liste de courses"
-#: stock/views.py:267
+#: stock/views.py:252
msgid " left"
msgstr " restant"
-#: stock/views.py:273
+#: stock/views.py:258
msgid ""
"Add here, items to buy that are not reference as a stock item (example : "
"sponge, knife, mugs ...)"
@@ -5984,44 +5988,44 @@ msgstr ""
"Ajouter ici les éléments non référencé comme élément de stock (example : "
"éponge, couteau, mugs ...)"
-#: stock/views.py:457
+#: stock/views.py:442
msgid " asked"
msgstr " demandé"
-#: stock/views.py:549
+#: stock/views.py:534
#, python-format
msgid "%(effective_quantity)s left"
msgstr "%(effective_quantity)s restant"
-#: subscription/models.py:43
+#: subscription/models.py:34
msgid "Bad subscription type"
msgstr "Mauvais type de cotisation"
-#: subscription/models.py:48
+#: subscription/models.py:39
msgid "Bad payment method"
msgstr "Mauvais type de paiement"
-#: subscription/models.py:56
+#: subscription/models.py:47
msgid "subscription type"
msgstr "type d'inscription"
-#: subscription/models.py:62
+#: subscription/models.py:53
msgid "subscription start"
msgstr "début de la cotisation"
-#: subscription/models.py:63
+#: subscription/models.py:54
msgid "subscription end"
msgstr "fin de la cotisation"
-#: subscription/models.py:72
+#: subscription/models.py:63
msgid "location"
msgstr "lieu"
-#: subscription/models.py:92
+#: subscription/models.py:83
msgid "You can not subscribe many time for the same period"
msgstr "Vous ne pouvez pas cotiser plusieurs fois pour la même période"
-#: subscription/models.py:97
+#: subscription/models.py:88
msgid "Subscription error"
msgstr "Erreur de cotisation"
@@ -6038,11 +6042,11 @@ msgid "Eboutic is reserved to specific users. In doubt, don't use it."
msgstr ""
"Eboutic est réservé à des cas particuliers. Dans le doute, ne l'utilisez pas."
-#: subscription/views.py:104
+#: subscription/views.py:94
msgid "A user with that email address already exists"
msgstr "Un utilisateur avec cette adresse email existe déjà"
-#: subscription/views.py:127
+#: subscription/views.py:117
msgid "You must either choose an existing user or create a new one properly"
msgstr ""
"Vous devez soit choisir un utilisateur existant, soit en créer un proprement"
@@ -6262,27 +6266,27 @@ msgstr ""
msgid "Edit comment"
msgstr "Éditer le commentaire"
-#: trombi/views.py:70
+#: trombi/views.py:69
msgid "My profile"
msgstr "Mon profil"
-#: trombi/views.py:77
+#: trombi/views.py:76
msgid "My pictures"
msgstr "Mes photos"
-#: trombi/views.py:89
+#: trombi/views.py:88
msgid "Admin tools"
msgstr "Admin Trombi"
-#: trombi/views.py:222
+#: trombi/views.py:220
msgid "Explain why you rejected the comment"
msgstr "Expliquez pourquoi vous refusez le commentaire"
-#: trombi/views.py:255
+#: trombi/views.py:253
msgid "Rejected comment"
msgstr "Commentaire rejeté"
-#: trombi/views.py:257
+#: trombi/views.py:255
#, python-format
msgid ""
"Your comment to %(target)s on the Trombi \"%(trombi)s\" was rejected for the "
@@ -6299,16 +6303,16 @@ msgstr ""
"\n"
"%(content)s"
-#: trombi/views.py:289
+#: trombi/views.py:287
#, python-format
msgid "%(name)s (deadline: %(date)s)"
msgstr "%(name)s (date limite: %(date)s)"
-#: trombi/views.py:299
+#: trombi/views.py:297
msgid "Select trombi"
msgstr "Choisir un trombi"
-#: trombi/views.py:301
+#: trombi/views.py:299
msgid ""
"This allows you to subscribe to a Trombi. Be aware that you can subscribe "
"only once, so don't play with that, or you will expose yourself to the "
@@ -6318,19 +6322,19 @@ msgstr ""
"pouvez vous inscrire qu'à un seul Trombi, donc ne jouez pas avec cet option "
"ou vous encourerez la colère des admins!"
-#: trombi/views.py:374
+#: trombi/views.py:372
msgid "Personal email (not UTBM)"
msgstr "Email personnel (pas UTBM)"
-#: trombi/views.py:375
+#: trombi/views.py:373
msgid "Phone"
msgstr "Téléphone"
-#: trombi/views.py:376
+#: trombi/views.py:374
msgid "Native town"
msgstr "Ville d'origine"
-#: trombi/views.py:489
+#: trombi/views.py:491
msgid ""
"You can not yet write comment, you must wait for the subscription deadline "
"to be passed."
@@ -6338,11 +6342,47 @@ msgstr ""
"Vous ne pouvez pas encore écrire de commentaires, vous devez attendre la fin "
"des inscriptions"
-#: trombi/views.py:496
+#: trombi/views.py:498
msgid "You can not write comment anymore, the deadline is already passed."
msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
-#: trombi/views.py:509
+#: trombi/views.py:511
#, python-format
msgid "Maximum characters: %(max_length)s"
msgstr "Nombre de caractères max: %(max_length)s"
+
+#~ msgid "Folder: "
+#~ msgstr "Dossier : "
+
+#~ msgid "File: "
+#~ msgstr "Fichier : "
+
+#~ msgid "Username"
+#~ msgstr "Nom d'utilisateur"
+
+#~ msgid "Password"
+#~ msgstr "Mot de passe"
+
+#~ msgid "Register a user"
+#~ msgstr "Enregistrer un utilisateur"
+
+#~ msgid "Current profile: "
+#~ msgstr "Profil actuel : "
+
+#~ msgid "Take picture"
+#~ msgstr "Prendre une photo"
+
+#~ msgid "Current avatar: "
+#~ msgstr "Avatar actuel : "
+
+#~ msgid "Current scrub: "
+#~ msgstr "Blouse actuelle : "
+
+#~ msgid "preview"
+#~ msgstr "miniature"
+
+#~ msgid "Rotate left"
+#~ msgstr "Tourner vers la gauche"
+
+#~ msgid "Rotate right"
+#~ msgstr "Tourner vers la droite"
diff --git a/matmat/admin.py b/matmat/admin.py
index 8c38f3f3..846f6b40 100644
--- a/matmat/admin.py
+++ b/matmat/admin.py
@@ -1,3 +1 @@
-from django.contrib import admin
-
# Register your models here.
diff --git a/matmat/models.py b/matmat/models.py
index 71a83623..6b202199 100644
--- a/matmat/models.py
+++ b/matmat/models.py
@@ -1,3 +1 @@
-from django.db import models
-
# Create your models here.
diff --git a/matmat/tests.py b/matmat/tests.py
index 7ce503c2..a39b155a 100644
--- a/matmat/tests.py
+++ b/matmat/tests.py
@@ -1,3 +1 @@
-from django.test import TestCase
-
# Create your tests here.
diff --git a/matmat/views.py b/matmat/views.py
index 10b2d4f5..eb769c62 100644
--- a/matmat/views.py
+++ b/matmat/views.py
@@ -24,19 +24,18 @@
from ast import literal_eval
from enum import Enum
-from django.views.generic import ListView, View
-from django.views.generic.edit import FormView
-from django.utils.translation import gettext_lazy as _
-from django.views.generic.detail import SingleObjectMixin
+from django import forms
from django.http.response import HttpResponseRedirect
from django.urls import reverse
-from django import forms
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import ListView, View
+from django.views.generic.detail import SingleObjectMixin
+from django.views.generic.edit import FormView
+from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
from core.models import User
-from core.views import FormerSubscriberMixin
+from core.views import FormerSubscriberMixin, search_user
from core.views.forms import SelectDate
-from core.views import search_user
-from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
# Enum to select search type
diff --git a/pedagogy/forms.py b/pedagogy/forms.py
index 6a3d29ce..28810e64 100644
--- a/pedagogy/forms.py
+++ b/pedagogy/forms.py
@@ -24,12 +24,9 @@
from django import forms
from django.utils.translation import gettext_lazy as _
-from django.forms.widgets import Widget
-from django.templatetags.static import static
-from core.views.forms import MarkdownInput
from core.models import User
-
+from core.views.forms import MarkdownInput
from pedagogy.models import UV, UVComment, UVCommentReport
diff --git a/pedagogy/migrations/0001_initial.py b/pedagogy/migrations/0001_initial.py
index e4285277..782e65c4 100644
--- a/pedagogy/migrations/0001_initial.py
+++ b/pedagogy/migrations/0001_initial.py
@@ -2,10 +2,10 @@
# Generated by Django 1.11.20 on 2019-07-05 14:32
from __future__ import unicode_literals
-from django.conf import settings
import django.core.validators
-from django.db import migrations, models
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/pedagogy/migrations/0003_alter_uv_language.py b/pedagogy/migrations/0003_alter_uv_language.py
new file mode 100644
index 00000000..02f1e798
--- /dev/null
+++ b/pedagogy/migrations/0003_alter_uv_language.py
@@ -0,0 +1,27 @@
+# Generated by Django 4.2 on 2024-06-26 09:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("pedagogy", "0002_auto_20190827_2251"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="uv",
+ name="language",
+ field=models.CharField(
+ choices=[
+ ("FR", "French"),
+ ("EN", "English"),
+ ("DE", "German"),
+ ("SP", "Spanish"),
+ ],
+ default="FR",
+ max_length=10,
+ verbose_name="language",
+ ),
+ ),
+ ]
diff --git a/pedagogy/models.py b/pedagogy/models.py
index 2278dda6..0de5c230 100644
--- a/pedagogy/models.py
+++ b/pedagogy/models.py
@@ -22,14 +22,13 @@
#
#
-from django.db import models
-from django.utils.translation import gettext_lazy as _
-from django.utils import timezone
-from django.core import validators
from django.conf import settings
-from django.utils.functional import cached_property
+from django.core import validators
+from django.db import models
from django.urls import reverse
-
+from django.utils import timezone
+from django.utils.functional import cached_property
+from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from core.models import User
diff --git a/pedagogy/search_indexes.py b/pedagogy/search_indexes.py
index 3ea75343..c6bc74af 100644
--- a/pedagogy/search_indexes.py
+++ b/pedagogy/search_indexes.py
@@ -23,7 +23,6 @@
#
from django.db import models
-
from haystack import indexes, signals
from core.search_indexes import BigCharFieldIndex
diff --git a/pedagogy/tests.py b/pedagogy/tests.py
index baee59bf..2c61facf 100644
--- a/pedagogy/tests.py
+++ b/pedagogy/tests.py
@@ -23,20 +23,21 @@
#
from django.conf import settings
+from django.core.management import call_command
from django.test import TestCase
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
-from django.core.management import call_command
-
-from core.models import User, Notification
+from core.models import Notification, User
from pedagogy.models import UV, UVComment, UVCommentReport
-def create_uv_template(user_id, code="IFC1", exclude_list=[]):
+def create_uv_template(user_id, code="IFC1", exclude_list=None):
"""
Factory to help UV creation/update in post requests
"""
+ if exclude_list is None:
+ exclude_list = []
uv = {
"code": code,
"author": user_id,
@@ -84,235 +85,218 @@ class UVCreation(TestCase):
"""
@classmethod
- def setUp(cls):
+ def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
+ cls.create_uv_url = reverse("pedagogy:uv_create")
+
+ def test_create_uv_admin_success(self):
+ self.client.force_login(self.bibou)
+ response = self.client.post(
+ self.create_uv_url, create_uv_template(self.bibou.id)
+ )
+ assert response.status_code == 302
+ assert UV.objects.filter(code="IFC1").exists()
+
+ def test_create_uv_pedagogy_admin_success(self):
+ self.client.force_login(self.tutu)
+ response = self.client.post(
+ self.create_uv_url, create_uv_template(self.tutu.id)
+ )
+ assert response.status_code == 302
+ assert UV.objects.filter(code="IFC1").exists()
+
+ def test_create_uv_unauthorized_fail(self):
+ # Test with anonymous user
+ response = self.client.post(self.create_uv_url, create_uv_template(0))
+ assert response.status_code == 403
+
+ # Test with subscribed user
+ self.client.force_login(self.sli)
+ response = self.client.post(self.create_uv_url, create_uv_template(self.sli.id))
+ assert response.status_code == 403
+
+ # Test with non subscribed user
+ self.client.force_login(self.guy)
+ response = self.client.post(self.create_uv_url, create_uv_template(self.guy.id))
+ assert response.status_code == 403
+
+ # Check that the UV has never been created
+ assert not UV.objects.filter(code="IFC1").exists()
+
+ def test_create_uv_bad_request_fail(self):
+ self.client.force_login(self.tutu)
+
+ # Test with wrong user id (if someone cheats on the hidden input)
+ response = self.client.post(
+ self.create_uv_url, create_uv_template(self.bibou.id)
+ )
+ assert response.status_code == 200
+
+ # Remove a required field
+ response = self.client.post(
+ self.create_uv_url,
+ create_uv_template(self.tutu.id, exclude_list=["title"]),
+ )
+ assert response.status_code == 200
+
+ # Check that the UV hase never been created
+ assert not UV.objects.filter(code="IFC1").exists()
+
+
+class UVListTest(TestCase):
+ """Test guide display rights."""
+
+ @classmethod
+ def setUpTestData(cls):
cls.bibou = User.objects.get(username="root")
cls.tutu = User.objects.get(username="tutu")
cls.sli = User.objects.get(username="sli")
cls.guy = User.objects.get(username="guy")
- def test_create_uv_admin_success(self):
- self.client.login(username="root", password="plop")
- response = self.client.post(
- reverse("pedagogy:uv_create"), create_uv_template(self.bibou.id)
- )
- self.assertEqual(response.status_code, 302)
- self.assertTrue(UV.objects.filter(code="IFC1").exists())
-
- def test_create_uv_pedagogy_admin_success(self):
- self.client.login(username="tutu", password="plop")
- response = self.client.post(
- reverse("pedagogy:uv_create"), create_uv_template(self.tutu.id)
- )
- self.assertEqual(response.status_code, 302)
- self.assertTrue(UV.objects.filter(code="IFC1").exists())
-
- def test_create_uv_unauthorized_fail(self):
- # Test with anonymous user
- response = self.client.post(
- reverse("pedagogy:uv_create"), create_uv_template(0)
- )
- self.assertEqual(response.status_code, 403)
-
- # Test with subscribed user
- self.client.login(username="sli", password="plop")
- response = self.client.post(
- reverse("pedagogy:uv_create"), create_uv_template(self.sli.id)
- )
- self.assertEqual(response.status_code, 403)
-
- # Test with non subscribed user
- self.client.login(username="guy", password="plop")
- response = self.client.post(
- reverse("pedagogy:uv_create"), create_uv_template(self.guy.id)
- )
- self.assertEqual(response.status_code, 403)
-
- # Check that the UV has never been created
- self.assertFalse(UV.objects.filter(code="IFC1").exists())
-
- def test_create_uv_bad_request_fail(self):
- self.client.login(username="tutu", password="plop")
-
- # Test with wrong user id (if someone cheats on the hidden input)
- response = self.client.post(
- reverse("pedagogy:uv_create"), create_uv_template(self.bibou.id)
- )
- self.assertNotEqual(response.status_code, 302)
- self.assertEqual(response.status_code, 200)
-
- # Remove a required field
- response = self.client.post(
- reverse("pedagogy:uv_create"),
- create_uv_template(self.tutu.id, exclude_list=["title"]),
- )
- self.assertNotEqual(response.status_code, 302)
- self.assertEqual(response.status_code, 200)
-
- # Check that the UV hase never been created
- self.assertFalse(UV.objects.filter(code="IFC1").exists())
-
-
-class UVListTest(TestCase):
- """
- Test guide display rights
- """
-
def test_uv_list_display_success(self):
# Display for root
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.get(reverse("pedagogy:guide"))
self.assertContains(response, text="PA00")
# Display for pedagogy admin
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
response = self.client.get(reverse("pedagogy:guide"))
self.assertContains(response, text="PA00")
# Display for simple subscriber
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
response = self.client.get(reverse("pedagogy:guide"))
self.assertContains(response, text="PA00")
def test_uv_list_display_fail(self):
# Don't display for anonymous user
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Don't display for none subscribed users
- self.client.login(username="guy", password="plop")
+ self.client.force_login(self.guy)
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
class UVDeleteTest(TestCase):
- """
- Test UV deletion rights
- """
+ """Test UV deletion rights."""
+
+ @classmethod
+ def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
+ cls.uv = UV.objects.get(code="PA00")
+ cls.delete_uv_url = reverse("pedagogy:uv_delete", kwargs={"uv_id": cls.uv.id})
def test_uv_delete_root_success(self):
- self.client.login(username="root", password="plop")
- self.client.post(
- reverse(
- "pedagogy:uv_delete", kwargs={"uv_id": UV.objects.get(code="PA00").id}
- )
- )
- self.assertFalse(UV.objects.filter(code="PA00").exists())
+ self.client.force_login(self.bibou)
+ self.client.post(self.delete_uv_url)
+ assert not UV.objects.filter(pk=self.uv.pk).exists()
def test_uv_delete_pedagogy_admin_success(self):
- self.client.login(username="tutu", password="plop")
- self.client.post(
- reverse(
- "pedagogy:uv_delete", kwargs={"uv_id": UV.objects.get(code="PA00").id}
- )
- )
- self.assertFalse(UV.objects.filter(code="PA00").exists())
+ self.client.force_login(self.tutu)
+ self.client.post(self.delete_uv_url)
+ assert not UV.objects.filter(pk=self.uv.pk).exists()
def test_uv_delete_pedagogy_unauthorized_fail(self):
# Anonymous user
- response = self.client.post(
- reverse(
- "pedagogy:uv_delete", kwargs={"uv_id": UV.objects.get(code="PA00").id}
- )
- )
- self.assertEqual(response.status_code, 403)
+ response = self.client.post(self.delete_uv_url)
+ assert response.status_code == 403
+ assert UV.objects.filter(pk=self.uv.pk).exists()
# Not subscribed user
- self.client.login(username="guy", password="plop")
- response = self.client.post(
- reverse(
- "pedagogy:uv_delete", kwargs={"uv_id": UV.objects.get(code="PA00").id}
- )
- )
- self.assertEqual(response.status_code, 403)
+ self.client.force_login(self.guy)
+ response = self.client.post(self.delete_uv_url)
+ assert response.status_code == 403
+ assert UV.objects.filter(pk=self.uv.pk).exists()
# Simply subscribed user
- self.client.login(username="sli", password="plop")
- response = self.client.post(
- reverse(
- "pedagogy:uv_delete", kwargs={"uv_id": UV.objects.get(code="PA00").id}
- )
- )
- self.assertEqual(response.status_code, 403)
-
- # Check that the UV still exists
- self.assertTrue(UV.objects.filter(code="PA00").exists())
+ self.client.force_login(self.sli)
+ response = self.client.post(self.delete_uv_url)
+ assert response.status_code == 403
+ assert UV.objects.filter(pk=self.uv.pk).exists()
class UVUpdateTest(TestCase):
- """
- Test UV update rights
- """
+ """Test UV update rights."""
- def setUp(self):
- self.bibou = User.objects.filter(username="root").first()
- self.tutu = User.objects.filter(username="tutu").first()
- self.uv = UV.objects.get(code="PA00")
+ @classmethod
+ def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
+ cls.uv = UV.objects.get(code="PA00")
+ cls.update_uv_url = reverse("pedagogy:uv_update", kwargs={"uv_id": cls.uv.id})
def test_uv_update_root_success(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
self.client.post(
- reverse("pedagogy:uv_update", kwargs={"uv_id": self.uv.id}),
- create_uv_template(self.bibou.id, code="PA00"),
+ self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
)
self.uv.refresh_from_db()
- self.assertEqual(self.uv.credit_type, "TM")
+ assert self.uv.credit_type == "TM"
def test_uv_update_pedagogy_admin_success(self):
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
self.client.post(
- reverse("pedagogy:uv_update", kwargs={"uv_id": self.uv.id}),
- create_uv_template(self.bibou.id, code="PA00"),
+ self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
)
self.uv.refresh_from_db()
- self.assertEqual(self.uv.credit_type, "TM")
+ assert self.uv.credit_type == "TM"
def test_uv_update_original_author_does_not_change(self):
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
response = self.client.post(
- reverse("pedagogy:uv_update", kwargs={"uv_id": self.uv.id}),
+ self.update_uv_url,
create_uv_template(self.tutu.id, code="PA00"),
)
-
+ assert response.status_code == 200
self.uv.refresh_from_db()
- self.assertEqual(response.status_code, 200)
- self.assertEqual(self.uv.author, self.bibou)
+ assert self.uv.author == self.bibou
def test_uv_update_pedagogy_unauthorized_fail(self):
# Anonymous user
response = self.client.post(
- reverse("pedagogy:uv_update", kwargs={"uv_id": self.uv.id}),
- create_uv_template(self.bibou.id, code="PA00"),
+ self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Not subscribed user
- self.client.login(username="guy", password="plop")
+ self.client.force_login(self.guy)
response = self.client.post(
- reverse("pedagogy:uv_update", kwargs={"uv_id": self.uv.id}),
- create_uv_template(self.bibou.id, code="PA00"),
+ self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Simply subscribed user
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
response = self.client.post(
- reverse("pedagogy:uv_update", kwargs={"uv_id": self.uv.id}),
- create_uv_template(self.bibou.id, code="PA00"),
+ self.update_uv_url, create_uv_template(self.bibou.id, code="PA00")
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Check that the UV has not changed
self.uv.refresh_from_db()
- self.assertEqual(self.uv.credit_type, "OM")
+ assert self.uv.credit_type == "OM"
# UVComment class tests
-def create_uv_comment_template(user_id, uv_code="PA00", exclude_list=[]):
+def create_uv_comment_template(user_id, uv_code="PA00", exclude_list=None):
"""
Factory to help UVComment creation/update in post requests
"""
+ if exclude_list is None:
+ exclude_list = []
comment = {
"author": user_id,
"uv": UV.objects.get(code=uv_code).id,
@@ -341,105 +325,80 @@ class UVCommentCreationAndDisplay(TestCase):
cls.sli = User.objects.get(username="sli")
cls.guy = User.objects.get(username="guy")
cls.uv = UV.objects.get(code="PA00")
+ cls.uv_url = reverse("pedagogy:uv_detail", kwargs={"uv_id": cls.uv.id})
def test_create_uv_comment_admin_success(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(self.bibou.id),
- )
- self.assertEqual(response.status_code, 302)
- response = self.client.get(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id})
+ self.uv_url, create_uv_comment_template(self.bibou.id)
)
+ self.assertRedirects(response, self.uv_url)
+ response = self.client.get(self.uv_url)
self.assertContains(response, text="Superbe UV")
def test_create_uv_comment_pedagogy_admin_success(self):
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(self.tutu.id),
- )
- self.assertEqual(response.status_code, 302)
- response = self.client.get(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id})
+ self.uv_url, create_uv_comment_template(self.tutu.id)
)
+ self.assertRedirects(response, self.uv_url)
+ response = self.client.get(self.uv_url)
self.assertContains(response, text="Superbe UV")
def test_create_uv_comment_subscriber_success(self):
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(self.sli.id),
- )
- self.assertEqual(response.status_code, 302)
- response = self.client.get(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id})
+ self.uv_url, create_uv_comment_template(self.sli.id)
)
+ self.assertRedirects(response, self.uv_url)
+ response = self.client.get(self.uv_url)
self.assertContains(response, text="Superbe UV")
def test_create_uv_comment_unauthorized_fail(self):
+ nb_comments = self.uv.comments.count()
# Test with anonymous user
- response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(0),
- )
- self.assertEqual(response.status_code, 403)
+ response = self.client.post(self.uv_url, create_uv_comment_template(0))
+ assert response.status_code == 403
# Test with non subscribed user
- self.client.login(username="guy", password="plop")
+ self.client.force_login(self.guy)
response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(self.guy.id),
+ self.uv_url, create_uv_comment_template(self.guy.id)
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
- # Check that the comment has never been created
- self.client.login(username="root", password="plop")
- response = self.client.get(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id})
- )
- self.assertNotContains(response, text="Superbe UV")
+ # Check that no comment has been created
+ assert self.uv.comments.count() == nb_comments
def test_create_uv_comment_bad_form_fail(self):
- self.client.login(username="root", password="plop")
+ nb_comments = self.uv.comments.count()
+ self.client.force_login(self.bibou)
response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
+ self.uv_url,
create_uv_comment_template(self.bibou.id, exclude_list=["grade_global"]),
)
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id})
- )
- self.assertNotContains(response, text="Superbe UV")
+ assert response.status_code == 200
+ assert self.uv.comments.count() == nb_comments
def test_create_uv_comment_twice_fail(self):
# Checks that the has_user_already_commented method works proprely
- self.assertFalse(self.uv.has_user_already_commented(self.bibou))
+ assert not self.uv.has_user_already_commented(self.bibou)
# Create a first comment
- self.client.login(username="root", password="plop")
- self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(self.bibou.id),
- )
+ self.client.force_login(self.bibou)
+ self.client.post(self.uv_url, create_uv_comment_template(self.bibou.id))
# Checks that the has_user_already_commented method works proprely
- self.assertTrue(self.uv.has_user_already_commented(self.bibou))
+ assert self.uv.has_user_already_commented(self.bibou)
# Create the second comment
comment = create_uv_comment_template(self.bibou.id)
comment["comment"] = "Twice"
- response = self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}), comment
- )
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- UVComment.objects.filter(comment__contains="Superbe UV").exists()
- )
- self.assertFalse(UVComment.objects.filter(comment__contains="Twice").exists())
+ response = self.client.post(self.uv_url, comment)
+ assert response.status_code == 200
+ assert UVComment.objects.filter(comment__contains="Superbe UV").exists()
+ assert not UVComment.objects.filter(comment__contains="Twice").exists()
self.assertContains(
response,
_(
@@ -449,21 +408,26 @@ class UVCommentCreationAndDisplay(TestCase):
# Ensure that there is no crash when no uv or no author is given
self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
- create_uv_comment_template(self.bibou.id, exclude_list=["uv"]),
+ self.uv_url, create_uv_comment_template(self.bibou.id, exclude_list=["uv"])
)
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
self.client.post(
- reverse("pedagogy:uv_detail", kwargs={"uv_id": self.uv.id}),
+ self.uv_url,
create_uv_comment_template(self.bibou.id, exclude_list=["author"]),
)
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
class UVCommentDeleteTest(TestCase):
- """
- Test UVComment deletion rights
- """
+ """Test UVComment deletion rights."""
+
+ @classmethod
+ def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
+ cls.krophil = User.objects.get(username="krophil")
def setUp(self):
comment_kwargs = create_uv_comment_template(
@@ -475,58 +439,60 @@ class UVCommentDeleteTest(TestCase):
self.comment.save()
def test_uv_comment_delete_root_success(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
self.client.post(
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
)
- self.assertFalse(UVComment.objects.filter(id=self.comment.id).exists())
+ assert not UVComment.objects.filter(id=self.comment.id).exists()
def test_uv_comment_delete_pedagogy_admin_success(self):
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
self.client.post(
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
)
- self.assertFalse(UVComment.objects.filter(id=self.comment.id).exists())
+ assert not UVComment.objects.filter(id=self.comment.id).exists()
def test_uv_comment_delete_author_success(self):
- self.client.login(username="krophil", password="plop")
+ self.client.force_login(self.krophil)
self.client.post(
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
)
- self.assertFalse(UVComment.objects.filter(id=self.comment.id).exists())
+ assert not UVComment.objects.filter(id=self.comment.id).exists()
def test_uv_comment_delete_unauthorized_fail(self):
# Anonymous user
response = self.client.post(
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Unsbscribed user
- self.client.login(username="guy", password="plop")
+ self.client.force_login(self.guy)
response = self.client.post(
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Subscribed user (not author of the comment)
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
response = self.client.post(
reverse("pedagogy:comment_delete", kwargs={"comment_id": self.comment.id})
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Check that the comment still exists
- self.assertTrue(UVComment.objects.filter(id=self.comment.id).exists())
+ assert UVComment.objects.filter(id=self.comment.id).exists()
class UVCommentUpdateTest(TestCase):
- """
- Test UVComment update rights
- """
+ """Test UVComment update rights."""
@classmethod
def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
cls.krophil = User.objects.get(username="krophil")
def setUp(self):
@@ -542,32 +508,32 @@ class UVCommentUpdateTest(TestCase):
self.comment_edit["comment"] = "Edited"
def test_uv_comment_update_root_success(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
self.comment.refresh_from_db()
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
def test_uv_comment_update_pedagogy_admin_success(self):
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
response = self.client.post(
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
self.comment.refresh_from_db()
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
def test_uv_comment_update_author_success(self):
- self.client.login(username="krophil", password="plop")
+ self.client.force_login(self.krophil)
response = self.client.post(
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
self.comment.refresh_from_db()
self.assertEqual(self.comment.comment, self.comment_edit["comment"])
@@ -577,35 +543,35 @@ class UVCommentUpdateTest(TestCase):
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Unsbscribed user
response = self.client.post(
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Subscribed user (not author of the comment)
response = self.client.post(
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Check that the comment hasn't change
self.comment.refresh_from_db()
self.assertNotEqual(self.comment.comment, self.comment_edit["comment"])
def test_uv_comment_update_original_author_does_not_change(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
self.comment_edit["author"] = User.objects.get(username="root").id
response = self.client.post(
reverse("pedagogy:comment_update", kwargs={"comment_id": self.comment.id}),
self.comment_edit,
)
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
self.assertEqual(self.comment.author, self.krophil)
@@ -615,37 +581,44 @@ class UVSearchTest(TestCase):
Test that the API is working well
"""
+ @classmethod
+ def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
+
def setUp(self):
call_command("update_index", "pedagogy")
def test_get_page_authorized_success(self):
# Test with root user
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
# Test with pedagogy admin
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
# Test with subscribed user
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
def test_get_page_unauthorized_fail(self):
# Test with anonymous user
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Test with not subscribed user
- self.client.login(username="guy", password="plop")
+ self.client.force_login(self.guy)
response = self.client.get(reverse("pedagogy:guide"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
def test_search_pa00_success(self):
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
# Search with UV code
response = self.client.get(reverse("pedagogy:guide"), {"search": "PA00"})
@@ -784,9 +757,15 @@ class UVModerationFormTest(TestCase):
Assert access rights and if the form works well
"""
- def setUp(self):
- self.krophil = User.objects.get(username="krophil")
+ @classmethod
+ def setUpTestData(cls):
+ cls.bibou = User.objects.get(username="root")
+ cls.tutu = User.objects.get(username="tutu")
+ cls.sli = User.objects.get(username="sli")
+ cls.guy = User.objects.get(username="guy")
+ cls.krophil = User.objects.get(username="krophil")
+ def setUp(self):
# Prepare a comment
comment_kwargs = create_uv_comment_template(self.krophil.id)
comment_kwargs["author"] = self.krophil
@@ -819,119 +798,109 @@ class UVModerationFormTest(TestCase):
def test_access_authorized_success(self):
# Test with root
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.get(reverse("pedagogy:moderation"))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
# Test with pedagogy admin
- self.client.login(username="tutu", password="plop")
+ self.client.force_login(self.tutu)
response = self.client.get(reverse("pedagogy:moderation"))
- self.assertEqual(response.status_code, 200)
+ assert response.status_code == 200
def test_access_unauthorized_fail(self):
# Test with anonymous user
response = self.client.get(reverse("pedagogy:moderation"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Test with unsubscribed user
- self.client.login(username="guy", password="plop")
+ self.client.force_login(self.guy)
response = self.client.get(reverse("pedagogy:moderation"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
# Test with subscribed user
- self.client.login(username="sli", password="plop")
+ self.client.force_login(self.sli)
response = self.client.get(reverse("pedagogy:moderation"))
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
def test_do_nothing(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(reverse("pedagogy:moderation"))
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that nothing has changed
- self.assertTrue(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists())
- self.assertTrue(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
- self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert UVComment.objects.filter(id=self.comment_1.id).exists()
+ assert UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
+ assert UVCommentReport.objects.filter(id=self.report_2.id).exists()
+ assert UVComment.objects.filter(id=self.comment_2.id).exists()
def test_delete_comment(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"), {"accepted_reports": [self.report_1.id]}
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that the comment and it's associated report has been deleted
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists())
+ assert not UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert not UVComment.objects.filter(id=self.comment_1.id).exists()
# Test that the bis report has been deleted
- self.assertFalse(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
+ assert not UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
# Test that the other comment and report still exists
- self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert UVCommentReport.objects.filter(id=self.report_2.id).exists()
+ assert UVComment.objects.filter(id=self.comment_2.id).exists()
def test_delete_comment_bulk(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"),
{"accepted_reports": [self.report_1.id, self.report_2.id]},
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that comments and their associated reports has been deleted
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists())
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_2.id).exists())
- self.assertFalse(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert not UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert not UVComment.objects.filter(id=self.comment_1.id).exists()
+ assert not UVCommentReport.objects.filter(id=self.report_2.id).exists()
+ assert not UVComment.objects.filter(id=self.comment_2.id).exists()
# Test that the bis report has been deleted
- self.assertFalse(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
+ assert not UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
def test_delete_comment_with_bis(self):
# Test case if two reports targets the same comment and are both deleted
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"),
{"accepted_reports": [self.report_1.id, self.report_1_bis.id]},
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that the comment and it's associated report has been deleted
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists())
+ assert not UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert not UVComment.objects.filter(id=self.comment_1.id).exists()
# Test that the bis report has been deleted
- self.assertFalse(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
+ assert not UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
def test_delete_report(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"), {"denied_reports": [self.report_1.id]}
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that the report has been deleted and that the comment still exists
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists())
+ assert not UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert UVComment.objects.filter(id=self.comment_1.id).exists()
# Test that the bis report is still there
- self.assertTrue(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
+ assert UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
# Test that the other comment and report still exists
- self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert UVCommentReport.objects.filter(id=self.report_2.id).exists()
+ assert UVComment.objects.filter(id=self.comment_2.id).exists()
def test_delete_report_bulk(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"),
{
@@ -942,21 +911,18 @@ class UVModerationFormTest(TestCase):
]
},
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that every reports has been deleted
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertFalse(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_2.id).exists())
-
+ assert not UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert not UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
+ assert not UVCommentReport.objects.filter(id=self.report_2.id).exists()
# Test that comments still exists
- self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert UVComment.objects.filter(id=self.comment_1.id).exists()
+ assert UVComment.objects.filter(id=self.comment_2.id).exists()
def test_delete_mixed(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"),
{
@@ -964,23 +930,21 @@ class UVModerationFormTest(TestCase):
"denied_reports": [self.report_1.id],
},
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that report 2 and his comment has been deleted
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_2.id).exists())
- self.assertFalse(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert not UVCommentReport.objects.filter(id=self.report_2.id).exists()
+ assert not UVComment.objects.filter(id=self.comment_2.id).exists()
# Test that report 1 has been deleted and it's comment still exists
- self.assertFalse(UVCommentReport.objects.filter(id=self.report_1.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_1.id).exists())
+ assert not UVCommentReport.objects.filter(id=self.report_1.id).exists()
+ assert UVComment.objects.filter(id=self.comment_1.id).exists()
# Test that report 1 bis is still there
- self.assertTrue(
- UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
- )
+ assert UVCommentReport.objects.filter(id=self.report_1_bis.id).exists()
def test_delete_mixed_with_bis(self):
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.bibou)
response = self.client.post(
reverse("pedagogy:moderation"),
{
@@ -988,21 +952,19 @@ class UVModerationFormTest(TestCase):
"denied_reports": [self.report_1_bis.id],
},
)
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
# Test that report 1 and 1 bis has been deleted
- self.assertFalse(
- UVCommentReport.objects.filter(
- id__in=[self.report_1.id, self.report_1_bis.id]
- ).exists()
- )
+ assert not UVCommentReport.objects.filter(
+ id__in=[self.report_1.id, self.report_1_bis.id]
+ ).exists()
# Test that comment 1 has been deleted
- self.assertFalse(UVComment.objects.filter(id=self.comment_1.id).exists())
+ assert not UVComment.objects.filter(id=self.comment_1.id).exists()
# Test that report and comment 2 still exists
- self.assertTrue(UVCommentReport.objects.filter(id=self.report_2.id).exists())
- self.assertTrue(UVComment.objects.filter(id=self.comment_2.id).exists())
+ assert UVCommentReport.objects.filter(id=self.report_2.id).exists()
+ assert UVComment.objects.filter(id=self.comment_2.id).exists()
class UVCommentReportCreateTest(TestCase):
@@ -1033,9 +995,9 @@ class UVCommentReportCreateTest(TestCase):
},
)
if success:
- self.assertEqual(response.status_code, 302)
+ assert response.status_code == 302
else:
- self.assertEqual(response.status_code, 403)
+ assert response.status_code == 403
self.assertEqual(UVCommentReport.objects.all().exists(), success)
def test_create_report_root_success(self):
@@ -1055,32 +1017,24 @@ class UVCommentReportCreateTest(TestCase):
reverse("pedagogy:comment_report", kwargs={"comment_id": self.comment.id}),
{"comment": self.comment.id, "reporter": 0, "reason": "C'est moche"},
)
- self.assertEqual(response.status_code, 403)
- self.assertFalse(UVCommentReport.objects.all().exists())
+ assert response.status_code == 403
+ assert not UVCommentReport.objects.all().exists()
def test_notifications(self):
- self.assertFalse(
- self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").exists()
- )
+ assert not self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").exists()
# Create a comment report
self.create_report_test("tutu", True)
# Check that a notification has been created for pedagogy admins
- self.assertTrue(
- self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").exists()
- )
+ assert self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").exists()
# Check that only pedagogy admins recieves this notification
for notif in Notification.objects.filter(type="PEDAGOGY_MODERATION").all():
- self.assertTrue(
- notif.user.is_in_group(pk=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID)
- )
+ assert notif.user.is_in_group(pk=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID)
# Check that notifications are not duplicated if not viewed
self.create_report_test("tutu", True)
- self.assertEqual(
- self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").count(), 1
- )
+ assert self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").count() == 1
# Check that a new notification is created when the old one has been viewed
notif = self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").first()
@@ -1089,6 +1043,4 @@ class UVCommentReportCreateTest(TestCase):
self.create_report_test("tutu", True)
- self.assertEqual(
- self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").count(), 2
- )
+ assert self.tutu.notifications.filter(type="PEDAGOGY_MODERATION").count() == 2
diff --git a/pedagogy/views.py b/pedagogy/views.py
index 13151c60..89187ca5 100644
--- a/pedagogy/views.py
+++ b/pedagogy/views.py
@@ -22,38 +22,35 @@
#
#
+from django.conf import settings
+from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
+from django.urls import reverse, reverse_lazy
+from django.utils import html
from django.views.generic import (
CreateView,
DeleteView,
- UpdateView,
- ListView,
FormView,
+ ListView,
+ UpdateView,
View,
)
-from django.utils import html
-from django.http import HttpResponse
-from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
-from django.urls import reverse_lazy, reverse
-from django.shortcuts import get_object_or_404
-from django.conf import settings
-
from haystack.query import SearchQuerySet
from rest_framework.renderers import JSONRenderer
+from core.models import Notification, RealGroup
from core.views import (
- DetailFormView,
CanCreateMixin,
- CanEditMixin,
- CanViewMixin,
CanEditPropMixin,
+ CanViewMixin,
+ DetailFormView,
)
-from core.models import RealGroup, Notification
-
from pedagogy.forms import (
- UVForm,
UVCommentForm,
- UVCommentReportForm,
UVCommentModerationForm,
+ UVCommentReportForm,
+ UVForm,
)
from pedagogy.models import UV, UVComment, UVCommentReport, UVSerializer
diff --git a/poetry.lock b/poetry.lock
index 23ef0bda..32fcaf72 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,229 +1,260 @@
-# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "alabaster"
-version = "0.7.12"
-description = "A configurable sidebar-enabled Sphinx theme"
-optional = true
-python-versions = "*"
-files = [
- {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
- {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
-]
-
-[[package]]
-name = "appnope"
-version = "0.1.3"
-description = "Disable App Nap on macOS >= 10.9"
+version = "0.7.16"
+description = "A light, configurable Sphinx theme"
optional = false
-python-versions = "*"
+python-versions = ">=3.9"
files = [
- {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
- {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
+ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"},
+ {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"},
]
[[package]]
name = "asgiref"
-version = "3.6.0"
+version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"},
- {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"},
+ {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
+ {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
]
+[package.dependencies]
+typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""}
+
[package.extras]
tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
-name = "babel"
-version = "2.11.0"
-description = "Internationalization utilities"
-optional = true
-python-versions = ">=3.6"
-files = [
- {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"},
- {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"},
-]
-
-[package.dependencies]
-pytz = ">=2015.7"
-
-[[package]]
-name = "backcall"
-version = "0.2.0"
-description = "Specifications for callback functions passed in to an API"
+name = "asttokens"
+version = "2.4.1"
+description = "Annotate AST trees with source code positions"
optional = false
python-versions = "*"
files = [
- {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
- {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
-]
-
-[[package]]
-name = "black"
-version = "23.3.0"
-description = "The uncompromising code formatter."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"},
- {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"},
- {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"},
- {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"},
- {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"},
- {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"},
- {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"},
- {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"},
- {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"},
- {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"},
- {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"},
- {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"},
- {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"},
- {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"},
- {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"},
- {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"},
- {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"},
- {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"},
- {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"},
- {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"},
- {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"},
- {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"},
- {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"},
- {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"},
- {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"},
+ {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
+ {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
]
[package.dependencies]
-click = ">=8.0.0"
-mypy-extensions = ">=0.4.3"
-packaging = ">=22.0"
-pathspec = ">=0.9.0"
-platformdirs = ">=2"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
+six = ">=1.12.0"
[package.extras]
-colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4)"]
-jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
-uvloop = ["uvloop (>=0.15.2)"]
+astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
+test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
+
+[[package]]
+name = "babel"
+version = "2.15.0"
+description = "Internationalization utilities"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"},
+ {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"},
+]
+
+[package.extras]
+dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
[[package]]
name = "certifi"
-version = "2022.12.7"
+version = "2024.7.4"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
- {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
- {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
+ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
+ {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
]
[[package]]
name = "cffi"
-version = "1.15.1"
+version = "1.16.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
-python-versions = "*"
+python-versions = ">=3.8"
files = [
- {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
- {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
- {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
- {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
- {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
- {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
- {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
- {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
- {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
- {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
- {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
- {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
- {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
- {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
- {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
- {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
- {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
- {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
- {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
+ {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
+ {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
+ {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
+ {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
+ {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
+ {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
+ {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
+ {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
+ {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
+ {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
+ {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
+ {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
+ {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
+ {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
+ {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
]
[package.dependencies]
pycparser = "*"
[[package]]
-name = "charset-normalizer"
-version = "2.1.1"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-optional = true
-python-versions = ">=3.6.0"
+name = "cfgv"
+version = "3.4.0"
+description = "Validate configuration and produce human readable error messages."
+optional = false
+python-versions = ">=3.8"
files = [
- {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
- {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
-[package.extras]
-unicode-backport = ["unicodedata2"]
-
[[package]]
-name = "click"
-version = "8.1.3"
-description = "Composable command line interface toolkit"
+name = "chardet"
+version = "5.2.0"
+description = "Universal encoding detector for Python 3"
optional = false
python-versions = ">=3.7"
files = [
- {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
- {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
+ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
+ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
]
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
+[[package]]
+name = "charset-normalizer"
+version = "3.3.2"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
+ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
+]
[[package]]
name = "colorama"
@@ -238,108 +269,124 @@ files = [
[[package]]
name = "coverage"
-version = "5.5"
+version = "7.5.4"
description = "Code coverage measurement for Python"
-optional = true
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-files = [
- {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
- {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
- {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
- {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
- {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
- {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
- {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
- {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
- {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
- {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
- {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
- {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
- {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
- {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
- {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
- {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
- {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
- {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
- {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
- {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
- {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
- {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
- {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
- {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
- {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
- {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
- {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
- {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
- {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
- {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
- {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
- {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
- {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
- {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
- {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
- {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
- {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
- {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
- {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
- {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
- {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
- {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
- {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
- {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
- {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
- {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
- {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
- {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
-]
-
-[package.extras]
-toml = ["toml"]
-
-[[package]]
-name = "cryptography"
-version = "40.0.1"
-description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "cryptography-40.0.1-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:918cb89086c7d98b1b86b9fdb70c712e5a9325ba6f7d7cfb509e784e0cfc6917"},
- {file = "cryptography-40.0.1-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9618a87212cb5200500e304e43691111570e1f10ec3f35569fdfcd17e28fd797"},
- {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a4805a4ca729d65570a1b7cac84eac1e431085d40387b7d3bbaa47e39890b88"},
- {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63dac2d25c47f12a7b8aa60e528bfb3c51c5a6c5a9f7c86987909c6c79765554"},
- {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a4e3406cfed6b1f6d6e87ed243363652b2586b2d917b0609ca4f97072994405"},
- {file = "cryptography-40.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1e0af458515d5e4028aad75f3bb3fe7a31e46ad920648cd59b64d3da842e4356"},
- {file = "cryptography-40.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d8aa3609d337ad85e4eb9bb0f8bcf6e4409bfb86e706efa9a027912169e89122"},
- {file = "cryptography-40.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cf91e428c51ef692b82ce786583e214f58392399cf65c341bc7301d096fa3ba2"},
- {file = "cryptography-40.0.1-cp36-abi3-win32.whl", hash = "sha256:650883cc064297ef3676b1db1b7b1df6081794c4ada96fa457253c4cc40f97db"},
- {file = "cryptography-40.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:a805a7bce4a77d51696410005b3e85ae2839bad9aa38894afc0aa99d8e0c3160"},
- {file = "cryptography-40.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd033d74067d8928ef00a6b1327c8ea0452523967ca4463666eeba65ca350d4c"},
- {file = "cryptography-40.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d36bbeb99704aabefdca5aee4eba04455d7a27ceabd16f3b3ba9bdcc31da86c4"},
- {file = "cryptography-40.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:32057d3d0ab7d4453778367ca43e99ddb711770477c4f072a51b3ca69602780a"},
- {file = "cryptography-40.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f5d7b79fa56bc29580faafc2ff736ce05ba31feaa9d4735048b0de7d9ceb2b94"},
- {file = "cryptography-40.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7c872413353c70e0263a9368c4993710070e70ab3e5318d85510cc91cce77e7c"},
- {file = "cryptography-40.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:28d63d75bf7ae4045b10de5413fb1d6338616e79015999ad9cf6fc538f772d41"},
- {file = "cryptography-40.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6f2bbd72f717ce33100e6467572abaedc61f1acb87b8d546001328d7f466b778"},
- {file = "cryptography-40.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cc3a621076d824d75ab1e1e530e66e7e8564e357dd723f2533225d40fe35c60c"},
- {file = "cryptography-40.0.1.tar.gz", hash = "sha256:2803f2f8b1e95f614419926c7e6f55d828afc614ca5ed61543877ae668cc3472"},
+ {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"},
+ {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"},
+ {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"},
+ {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"},
+ {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"},
+ {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"},
+ {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"},
+ {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"},
+ {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"},
+ {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"},
+ {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"},
+ {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"},
+ {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"},
+ {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"},
+ {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"},
+ {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"},
+ {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"},
+ {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"},
+ {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"},
+ {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"},
+ {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"},
+ {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"},
+ {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"},
+ {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"},
+ {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"},
+ {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"},
+ {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"},
+ {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"},
+ {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"},
+ {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"},
+ {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"},
+ {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"},
+ {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"},
+ {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"},
+ {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"},
+ {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"},
+ {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"},
+ {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"},
+ {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"},
+ {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"},
+ {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"},
+ {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"},
+ {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"},
+ {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"},
+ {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"},
+ {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"},
+ {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"},
+ {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"},
+ {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"},
+ {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"},
+ {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"},
+ {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"},
]
[package.dependencies]
-cffi = ">=1.12"
+tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "cryptography"
+version = "42.0.8"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"},
+ {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"},
+ {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"},
+ {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"},
+ {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"},
+ {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"},
+ {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"},
+ {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"},
+ {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"},
+ {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"},
+ {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"},
+ {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"},
+ {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"},
+ {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"},
+ {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"},
+ {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"},
+ {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"},
+ {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"},
+ {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"},
+ {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"},
+ {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"},
+ {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"},
+ {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"},
+ {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"},
+ {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"},
+ {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"},
+ {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"},
+ {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"},
+ {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"},
+ {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"},
+ {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"},
+ {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"},
+]
+
+[package.dependencies]
+cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
-docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
-pep8test = ["black", "check-manifest", "mypy", "ruff"]
-sdist = ["setuptools-rust (>=0.11.4)"]
+docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
+nox = ["nox"]
+pep8test = ["check-sdist", "click", "mypy", "ruff"]
+sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
-test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"]
+test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
-tox = ["tox"]
[[package]]
name = "decorator"
@@ -354,33 +401,44 @@ files = [
[[package]]
name = "dict2xml"
-version = "1.7.3"
+version = "1.7.5"
description = "Small utility to convert a python dictionary into an XML string"
optional = false
python-versions = ">=3.5"
files = [
- {file = "dict2xml-1.7.3-py3-none-any.whl", hash = "sha256:f849e1aec277f93d087482461b6b8afdde61df346918298aca4c42bcf9895f6d"},
- {file = "dict2xml-1.7.3.tar.gz", hash = "sha256:02a5c198d0fecdfeb52644e9d905200a36c031e11c201362d7d217df684bc15d"},
+ {file = "dict2xml-1.7.5-py3-none-any.whl", hash = "sha256:f5380dcda0039807bff5543801009f36e5bfff355705863628835cb69f4711b6"},
+ {file = "dict2xml-1.7.5.tar.gz", hash = "sha256:e279f4707cf7733f1e56b2cea39e257c727b86f74e449deccc6a712a1cfe4e45"},
]
[package.extras]
-tests = ["noseofyeti[black] (==2.4.1)", "pytest (==7.2.1)"]
+tests = ["noseofyeti[black] (==2.4.4)", "pytest (==7.4.4)"]
+
+[[package]]
+name = "distlib"
+version = "0.3.8"
+description = "Distribution utilities"
+optional = false
+python-versions = "*"
+files = [
+ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
+ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
+]
[[package]]
name = "django"
-version = "3.2.18"
-description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
+version = "4.2.13"
+description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "Django-3.2.18-py3-none-any.whl", hash = "sha256:4d492d9024c7b3dfababf49f94511ab6a58e2c9c3c7207786f1ba4eb77750706"},
- {file = "Django-3.2.18.tar.gz", hash = "sha256:08208dfe892eb64fff073ca743b3b952311104f939e7f6dae954fe72dcc533ba"},
+ {file = "Django-4.2.13-py3-none-any.whl", hash = "sha256:a17fcba2aad3fc7d46fdb23215095dbbd64e6174bf4589171e732b18b07e426a"},
+ {file = "Django-4.2.13.tar.gz", hash = "sha256:837e3cf1f6c31347a1396a3f6b65688f2b4bb4a11c580dcb628b5afe527b68a5"},
]
[package.dependencies]
-asgiref = ">=3.3.2,<4"
-pytz = "*"
-sqlparse = ">=0.2.2"
+asgiref = ">=3.6.0,<4"
+sqlparse = ">=0.3.1"
+tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
argon2 = ["argon2-cffi (>=19.1.0)"]
@@ -388,23 +446,23 @@ bcrypt = ["bcrypt"]
[[package]]
name = "django-ajax-selects"
-version = "2.2.0"
+version = "2.2.1"
description = "Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete."
optional = false
python-versions = "*"
files = [
- {file = "django-ajax-selects-2.2.0.tar.gz", hash = "sha256:539298874b2d26ce9e778a5173d312f55340c887a126c7e2d3460b9a5b4593a2"},
+ {file = "django-ajax-selects-2.2.1.tar.gz", hash = "sha256:996ffb38dff1a621b358613afdf2681dbf261e5976da3c30a75e9b08fd81a887"},
]
[[package]]
name = "django-countries"
-version = "7.5.1"
+version = "7.6.1"
description = "Provides a country field for Django models."
optional = false
python-versions = "*"
files = [
- {file = "django-countries-7.5.1.tar.gz", hash = "sha256:22915d9b9403932b731622619940a54894a3eb0da9a374e7249c8fc453c122d7"},
- {file = "django_countries-7.5.1-py3-none-any.whl", hash = "sha256:2df707aca7a5e677254bed116cf6021a136ebaccd5c2f46860abd6452bb45521"},
+ {file = "django-countries-7.6.1.tar.gz", hash = "sha256:c772d4e3e54afcc5f97a018544e96f246c6d9f1db51898ab0c15cd57e19437cf"},
+ {file = "django_countries-7.6.1-py3-none-any.whl", hash = "sha256:1ed20842fe0f6194f91faca21076649513846a8787c9eb5aeec3cbe1656b8acc"},
]
[package.dependencies]
@@ -412,20 +470,20 @@ asgiref = "*"
typing-extensions = "*"
[package.extras]
-dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox"]
-maintainer = ["django", "transifex-client", "zest.releaser[recommended]"]
+dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox (==4.*)"]
+maintainer = ["django", "zest.releaser[recommended]"]
pyuca = ["pyuca"]
test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytest-django"]
[[package]]
name = "django-debug-toolbar"
-version = "4.0.0"
+version = "4.3.0"
description = "A configurable set of panels that display various debug information about the current request/response."
optional = false
python-versions = ">=3.8"
files = [
- {file = "django_debug_toolbar-4.0.0-py3-none-any.whl", hash = "sha256:bad339d68520652ddc1580c76f136fcbc3e020fd5ed96510a89a02ec81bb3fb1"},
- {file = "django_debug_toolbar-4.0.0.tar.gz", hash = "sha256:89619f6e0ea1057dca47bfc429ed99b237ef70074dabc065a7faa5f00e1459cf"},
+ {file = "django_debug_toolbar-4.3.0-py3-none-any.whl", hash = "sha256:e09b7dcb8417b743234dfc57c95a7c1d1d87a88844abd13b4c5387f807b31bf6"},
+ {file = "django_debug_toolbar-4.3.0.tar.gz", hash = "sha256:0b0dddee5ea29b9cb678593bc0d7a6d76b21d7799cb68e091a2148341a80f3c4"},
]
[package.dependencies]
@@ -434,33 +492,35 @@ sqlparse = ">=0.2"
[[package]]
name = "django-haystack"
-version = "3.2.1"
+version = "3.3.0"
description = "Pluggable search for Django."
optional = false
python-versions = "*"
files = [
- {file = "django-haystack-3.2.1.tar.gz", hash = "sha256:97e3197aefc225fe405b6f17600a2534bf827cb4d6743130c20bc1a06f7293a4"},
+ {file = "django_haystack-3.3.0.tar.gz", hash = "sha256:e3ceed6b8000625da14d409eb4dac69894905e2ac8ac18f9bfdb59323ca02eab"},
]
[package.dependencies]
-Django = ">=2.2"
+Django = ">=3.2"
+packaging = "*"
[package.extras]
elasticsearch = ["elasticsearch (>=5,<8)"]
+testing = ["coverage", "geopy (==2)", "pysolr (>=3.7)", "python-dateutil", "requests", "whoosh (>=2.5.4,<3.0)"]
[[package]]
name = "django-jinja"
-version = "2.10.2"
+version = "2.11.0"
description = "Jinja2 templating language integrated in Django."
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "django-jinja-2.10.2.tar.gz", hash = "sha256:bfdfbb55c1f5a679d69ad575d550c4707d386634009152efe014089f3c4d1412"},
- {file = "django_jinja-2.10.2-py3-none-any.whl", hash = "sha256:dd003ec1c95c0989eb28a538831bced62b1b61da551cb44a5dfd708fcf75589f"},
+ {file = "django-jinja-2.11.0.tar.gz", hash = "sha256:47c06d3271e6b2f27d3596278af517bfe2e19c1eb36ae1c0b1cc302d7f0259af"},
+ {file = "django_jinja-2.11.0-py3-none-any.whl", hash = "sha256:cc4c72246a6e346aa0574e0c56c3e534c1a20ef47b8476f05d7287781f69a0a9"},
]
[package.dependencies]
-django = ">=2.2"
+django = ">=3.2"
jinja2 = ">=3"
[[package]]
@@ -507,17 +567,17 @@ django = "*"
[[package]]
name = "django-simple-captcha"
-version = "0.5.17"
+version = "0.5.20"
description = "A very simple, yet powerful, Django captcha application"
optional = false
python-versions = "*"
files = [
- {file = "django-simple-captcha-0.5.17.tar.gz", hash = "sha256:9649e66dab4e71efacbfef02f48b83b91684898352a1ab56f1686ce71033b328"},
- {file = "django_simple_captcha-0.5.17-py2.py3-none-any.whl", hash = "sha256:f9a07e5e9de264ba4039c9eaad66bc48188a21ceda5fcdc2fa13c5512141c2c9"},
+ {file = "django-simple-captcha-0.5.20.tar.gz", hash = "sha256:20273009a7beb44297e9f6c7a6bd21ada3d2fa93c314d2f6bf5e394ceeb6a297"},
+ {file = "django_simple_captcha-0.5.20-py2.py3-none-any.whl", hash = "sha256:3359cb033c489eae6544a80ad92517db3d35b3b328b3b427393399c3d7f55275"},
]
[package.dependencies]
-Django = ">=2.2"
+Django = ">=3.2"
django-ranged-response = "0.2.0"
Pillow = ">=6.2.0"
@@ -526,60 +586,117 @@ test = ["testfixtures"]
[[package]]
name = "djangorestframework"
-version = "3.14.0"
+version = "3.15.2"
description = "Web APIs for Django, made easy."
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"},
- {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"},
+ {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"},
+ {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"},
]
[package.dependencies]
-django = ">=3.0"
-pytz = "*"
+django = ">=4.2"
[[package]]
name = "docutils"
-version = "0.17.1"
+version = "0.18.1"
description = "Docutils -- Python Documentation Utilities"
-optional = true
+optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
- {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
- {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
+ {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"},
+ {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"},
]
+[[package]]
+name = "exceptiongroup"
+version = "1.2.1"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
+ {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "executing"
+version = "2.0.1"
+description = "Get the currently executing AST node of a frame, and other information"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"},
+ {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"},
+]
+
+[package.extras]
+tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
+
+[[package]]
+name = "filelock"
+version = "3.15.4"
+description = "A platform independent file lock."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"},
+ {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"]
+typing = ["typing-extensions (>=4.8)"]
+
[[package]]
name = "freezegun"
-version = "1.2.2"
+version = "1.5.1"
description = "Let your Python tests travel through time"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"},
- {file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"},
+ {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"},
+ {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"},
]
[package.dependencies]
python-dateutil = ">=2.7"
+[[package]]
+name = "identify"
+version = "2.5.36"
+description = "File identification library for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"},
+ {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"},
+]
+
+[package.extras]
+license = ["ukkonen"]
+
[[package]]
name = "idna"
-version = "3.4"
+version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
-optional = true
+optional = false
python-versions = ">=3.5"
files = [
- {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
- {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
-optional = true
+optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
@@ -587,88 +704,82 @@ files = [
]
[[package]]
-name = "importlib-metadata"
-version = "6.0.0"
-description = "Read metadata from Python packages"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
- {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
-]
-
-[package.dependencies]
-zipp = ">=0.5"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-perf = ["ipython"]
-testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
-
-[[package]]
-name = "ipython"
-version = "7.34.0"
-description = "IPython: Productive Interactive Computing"
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
files = [
- {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"},
- {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"},
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "ipython"
+version = "8.26.0"
+description = "IPython: Productive Interactive Computing"
+optional = false
+python-versions = ">=3.10"
+files = [
+ {file = "ipython-8.26.0-py3-none-any.whl", hash = "sha256:e6b347c27bdf9c32ee9d31ae85defc525755a1869f14057e900675b9e8d6e6ff"},
+ {file = "ipython-8.26.0.tar.gz", hash = "sha256:1cec0fbba8404af13facebe83d04436a7434c7400e59f47acf467c64abd0956c"},
]
[package.dependencies]
-appnope = {version = "*", markers = "sys_platform == \"darwin\""}
-backcall = "*"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
decorator = "*"
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
jedi = ">=0.16"
matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
-pickleshare = "*"
-prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
-pygments = "*"
-setuptools = ">=18.5"
-traitlets = ">=4.2"
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
+prompt-toolkit = ">=3.0.41,<3.1.0"
+pygments = ">=2.4.0"
+stack-data = "*"
+traitlets = ">=5.13.0"
+typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
[package.extras]
-all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
-doc = ["Sphinx (>=1.3)"]
+all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
+black = ["black"]
+doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"]
kernel = ["ipykernel"]
+matplotlib = ["matplotlib"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
notebook = ["ipywidgets", "notebook"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
-test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"]
+test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
+test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
[[package]]
name = "jedi"
-version = "0.18.2"
+version = "0.19.1"
description = "An autocompletion tool for Python that can be used for text editors."
optional = false
python-versions = ">=3.6"
files = [
- {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"},
- {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"},
+ {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
+ {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
]
[package.dependencies]
-parso = ">=0.8.0,<0.9.0"
+parso = ">=0.8.3,<0.9.0"
[package.extras]
docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
+qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
+testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]]
name = "jinja2"
-version = "3.1.2"
+version = "3.1.4"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
- {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
- {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
+ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
+ {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[package.dependencies]
@@ -688,67 +799,88 @@ files = [
{file = "libsass-0.22.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:081e256ab3c5f3f09c7b8dea3bf3bf5e64a97c6995fd9eea880639b3f93a9f9a"},
{file = "libsass-0.22.0-cp37-abi3-win32.whl", hash = "sha256:89c5ce497fcf3aba1dd1b19aae93b99f68257e5f2026b731b00a872f13324c7f"},
{file = "libsass-0.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:65455a2728b696b62100eb5932604aa13a29f4ac9a305d95773c14aaa7200aaf"},
+ {file = "libsass-0.22.0-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:5fb2297a4754a6c8e25cfe5c015a3b51a2b6b9021b333f989bb8ce9d60eb5828"},
{file = "libsass-0.22.0.tar.gz", hash = "sha256:3ab5ad18e47db560f4f0c09e3d28cf3bb1a44711257488ac2adad69f4f7f8425"},
]
[[package]]
name = "markupsafe"
-version = "2.1.1"
+version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.7"
files = [
- {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
- {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
+ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
]
[[package]]
name = "matplotlib-inline"
-version = "0.1.6"
+version = "0.1.7"
description = "Inline Matplotlib backend for Jupyter"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
files = [
- {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
- {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
+ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
+ {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
]
[package.dependencies]
@@ -766,62 +898,51 @@ files = [
]
[[package]]
-name = "mypy-extensions"
-version = "0.4.3"
-description = "Experimental type system extensions for programs checked with the mypy typechecker."
+name = "nodeenv"
+version = "1.9.1"
+description = "Node.js virtual environment builder"
optional = false
-python-versions = "*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
- {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
- {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
+ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
+ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
]
[[package]]
name = "packaging"
-version = "23.0"
+version = "24.1"
description = "Core utilities for Python packages"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"},
- {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
+ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "parso"
-version = "0.8.3"
+version = "0.8.4"
description = "A Python Parser"
optional = false
python-versions = ">=3.6"
files = [
- {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
- {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
+ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
+ {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
]
[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["docopt", "pytest (<6.0.0)"]
-
-[[package]]
-name = "pathspec"
-version = "0.10.3"
-description = "Utility library for gitignore style pattern matching of file paths."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"},
- {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"},
-]
+qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
+testing = ["docopt", "pytest"]
[[package]]
name = "pexpect"
-version = "4.8.0"
+version = "4.9.0"
description = "Pexpect allows easy control of interactive console applications."
optional = false
python-versions = "*"
files = [
- {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
- {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
+ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
+ {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
]
[package.dependencies]
@@ -829,140 +950,170 @@ ptyprocess = ">=0.5"
[[package]]
name = "phonenumbers"
-version = "8.13.4"
+version = "8.13.40"
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
optional = false
python-versions = "*"
files = [
- {file = "phonenumbers-8.13.4-py2.py3-none-any.whl", hash = "sha256:a577a46c069ad889c7b7cf4dd978751d059edeab28b97acead4775d2ea1fc70a"},
- {file = "phonenumbers-8.13.4.tar.gz", hash = "sha256:6d63455012fc9431105ffc7739befca61c3efc551b287dca58d2be2e745475a9"},
-]
-
-[[package]]
-name = "pickleshare"
-version = "0.7.5"
-description = "Tiny 'shelve'-like database with concurrency support"
-optional = false
-python-versions = "*"
-files = [
- {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
- {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
+ {file = "phonenumbers-8.13.40-py2.py3-none-any.whl", hash = "sha256:9582752c20a1da5ec4449f7f97542bf8a793c8e2fec0ab57f767177bb8fc0b1d"},
+ {file = "phonenumbers-8.13.40.tar.gz", hash = "sha256:f137c2848b8e83dd064b71881b65680584417efa202177fd330e2f7ff6c68113"},
]
[[package]]
name = "pillow"
-version = "9.4.0"
+version = "10.4.0"
description = "Python Imaging Library (Fork)"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"},
- {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"},
- {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"},
- {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"},
- {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"},
- {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"},
- {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"},
- {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"},
- {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"},
- {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"},
- {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"},
- {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"},
- {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"},
- {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"},
- {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"},
- {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"},
- {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"},
- {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d"},
- {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57"},
- {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5"},
- {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070"},
- {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28"},
- {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35"},
- {file = "Pillow-9.4.0-cp310-cp310-win32.whl", hash = "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a"},
- {file = "Pillow-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391"},
- {file = "Pillow-9.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133"},
- {file = "Pillow-9.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132"},
- {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0"},
- {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35"},
- {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab"},
- {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4"},
- {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d"},
- {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8"},
- {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a"},
- {file = "Pillow-9.4.0-cp311-cp311-win32.whl", hash = "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c"},
- {file = "Pillow-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee"},
- {file = "Pillow-9.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493"},
- {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327"},
- {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe"},
- {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57"},
- {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4"},
- {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5"},
- {file = "Pillow-9.4.0-cp37-cp37m-win32.whl", hash = "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e"},
- {file = "Pillow-9.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6"},
- {file = "Pillow-9.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9"},
- {file = "Pillow-9.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011"},
- {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df"},
- {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837"},
- {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b"},
- {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d"},
- {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b"},
- {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f"},
- {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628"},
- {file = "Pillow-9.4.0-cp38-cp38-win32.whl", hash = "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d"},
- {file = "Pillow-9.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a"},
- {file = "Pillow-9.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569"},
- {file = "Pillow-9.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed"},
- {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815"},
- {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264"},
- {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e"},
- {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503"},
- {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6"},
- {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2"},
- {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153"},
- {file = "Pillow-9.4.0-cp39-cp39-win32.whl", hash = "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c"},
- {file = "Pillow-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b"},
- {file = "Pillow-9.4.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5"},
- {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286"},
- {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd"},
- {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df"},
- {file = "Pillow-9.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336"},
- {file = "Pillow-9.4.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3"},
- {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa"},
- {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb"},
- {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a"},
- {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"},
- {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"},
+ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
+ {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
+ {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"},
+ {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"},
+ {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"},
+ {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"},
+ {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"},
+ {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"},
+ {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"},
+ {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"},
+ {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"},
+ {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"},
+ {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"},
+ {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"},
+ {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"},
+ {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"},
+ {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"},
+ {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"},
+ {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"},
+ {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"},
+ {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"},
+ {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"},
+ {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"},
+ {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"},
+ {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"},
+ {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"},
+ {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"},
+ {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"},
+ {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"},
+ {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"},
+ {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"},
+ {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"},
+ {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"},
+ {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"},
+ {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"},
+ {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"},
+ {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"},
+ {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"},
+ {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"},
+ {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"},
+ {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"},
+ {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"},
+ {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"},
+ {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"},
+ {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"},
+ {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"},
+ {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"},
+ {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"},
+ {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"},
+ {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"},
+ {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"},
+ {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"},
+ {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"},
+ {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"},
+ {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"},
+ {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"},
+ {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"},
+ {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"},
+ {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"},
+ {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"},
+ {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"},
+ {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"},
+ {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"},
+ {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"},
+ {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"},
+ {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"},
+ {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"},
+ {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"},
]
[package.extras]
-docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"]
+docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
+fpx = ["olefile"]
+mic = ["olefile"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
+typing = ["typing-extensions"]
+xmp = ["defusedxml"]
[[package]]
name = "platformdirs"
-version = "2.6.2"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+version = "4.2.2"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},
- {file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"},
+ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
+ {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
]
[package.extras]
-docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
+docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
+type = ["mypy (>=1.8)"]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pre-commit"
+version = "3.7.1"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"},
+ {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"},
+]
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+virtualenv = ">=20.10.0"
[[package]]
name = "prompt-toolkit"
-version = "3.0.36"
+version = "3.0.47"
description = "Library for building powerful interactive command lines in Python"
optional = false
-python-versions = ">=3.6.2"
+python-versions = ">=3.7.0"
files = [
- {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"},
- {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"},
+ {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"},
+ {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"},
]
[package.dependencies]
@@ -970,70 +1121,83 @@ wcwidth = "*"
[[package]]
name = "psycopg2-binary"
-version = "2.9.3"
+version = "2.9.9"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f2534ab7dc7e776a263b463a16e189eb30e85ec9bbe1bff9e78dae802608932"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"},
- {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"},
- {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"},
- {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e6aa71ae45f952a2205377773e76f4e3f27951df38e69a4c95440c779e013560"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"},
- {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3a24a1982ae56461cc24f6680604fffa2c1b818e9dc55680da038792e004d18"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"},
- {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"},
+ {file = "psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-win32.whl", hash = "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682"},
+ {file = "psycopg2_binary-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"},
+ {file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692"},
+ {file = "psycopg2_binary-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-win32.whl", hash = "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5"},
+ {file = "psycopg2_binary-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-win32.whl", hash = "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90"},
+ {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"},
]
[[package]]
@@ -1047,68 +1211,122 @@ files = [
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
+[[package]]
+name = "pure-eval"
+version = "0.2.2"
+description = "Safely evaluate AST nodes without side effects"
+optional = false
+python-versions = "*"
+files = [
+ {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
+ {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
+]
+
+[package.extras]
+tests = ["pytest"]
+
[[package]]
name = "pycparser"
-version = "2.21"
+version = "2.22"
description = "C parser in Python"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.8"
files = [
- {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
- {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
+ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[[package]]
name = "pygments"
-version = "2.14.0"
+version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
-python-versions = ">=3.6"
-files = [
- {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"},
- {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"},
-]
-
-[package.extras]
-plugins = ["importlib-metadata"]
-
-[[package]]
-name = "pygraphviz"
-version = "1.10"
-description = "Python interface to Graphviz"
-optional = false
python-versions = ">=3.8"
files = [
- {file = "pygraphviz-1.10.zip", hash = "sha256:457e093a888128903251a266a8cc16b4ba93f3f6334b3ebfed92c7471a74d867"},
+ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
+]
+
+[package.extras]
+windows-terminal = ["colorama (>=0.4.6)"]
+
+[[package]]
+name = "pygraphviz"
+version = "1.13"
+description = "Python interface to Graphviz"
+optional = false
+python-versions = ">=3.10"
+files = [
+ {file = "pygraphviz-1.13.tar.gz", hash = "sha256:6ad8aa2f26768830a5a1cfc8a14f022d13df170a8f6fdfd68fd1aa1267000964"},
]
[[package]]
-name = "pyopenssl"
-version = "23.1.1"
-description = "Python wrapper module around the OpenSSL library"
+name = "pytest"
+version = "8.2.2"
+description = "pytest: simple powerful testing with Python"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "pyOpenSSL-23.1.1-py3-none-any.whl", hash = "sha256:9e0c526404a210df9d2b18cd33364beadb0dc858a739b885677bc65e105d4a4c"},
- {file = "pyOpenSSL-23.1.1.tar.gz", hash = "sha256:841498b9bec61623b1b6c47ebbc02367c07d60e0e195f19790817f10cc8db0b7"},
+ {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
+ {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
]
[package.dependencies]
-cryptography = ">=38.0.0,<41"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.5,<2.0"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
-docs = ["sphinx (!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"]
-test = ["flaky", "pretend", "pytest (>=3.0.1)"]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-cov"
+version = "5.0.0"
+description = "Pytest plugin for measuring coverage."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
+ {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
+]
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
+
+[[package]]
+name = "pytest-django"
+version = "4.8.0"
+description = "A Django plugin for pytest."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"},
+ {file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"},
+]
+
+[package.dependencies]
+pytest = ">=7.0.0"
+
+[package.extras]
+docs = ["sphinx", "sphinx-rtd-theme"]
+testing = ["Django", "django-configurations (>=2.0)"]
[[package]]
name = "python-dateutil"
-version = "2.8.2"
+version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
- {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
- {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
+ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
@@ -1125,97 +1343,142 @@ files = [
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
]
+[[package]]
+name = "pyyaml"
+version = "6.0.1"
+description = "YAML parser and emitter for Python"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
+ {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
+ {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
+ {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
+ {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
+ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+]
+
[[package]]
name = "reportlab"
-version = "3.6.12"
+version = "4.2.2"
description = "The Reportlab Toolkit"
optional = false
-python-versions = ">=3.7,<4"
+python-versions = "<4,>=3.7"
files = [
- {file = "reportlab-3.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dfcf7bd6db5d80711cbbd0996b6e7a79cc414ca81457960367df11d2860f92a"},
- {file = "reportlab-3.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0bc7a1d64fe754b62e175ba0cf47a630b529c0488ec9ac4e4c7655e295ea4d"},
- {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adf78ccb2defad5b6ecb2e2e9f2a672719b0a8e2278592a7d77f6c220a042388"},
- {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84afd5bef6e407c80ba9f99b6abbe3ea78e8243b0f19897a871a7bcad1f749d"},
- {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4fa3cdf490f3828b055381e8c7dc7819b3e5f7a442d7af7a8f90e9806a7fff51"},
- {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07fdd968df7941c2bfb67b9bb4532f424992dfafc71b72a4e4b291ff707e6b0e"},
- {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce85a204f46c871c8af6fa64b9bbed165456935c1d0bfb2f570a3194f6723ddb"},
- {file = "reportlab-3.6.12-cp310-cp310-win32.whl", hash = "sha256:090ea99ff829d918f7b6140594373b1340a34e1e6876eddae5aa06662ec10d64"},
- {file = "reportlab-3.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:4c599645af9b5b2241a23e977a82c965a59c24cd94b2600b8d34373c66cad763"},
- {file = "reportlab-3.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:236a6483210049205f6180d7a7595d0ca2e4ce343d83cc94ca719a4145809c6f"},
- {file = "reportlab-3.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69f41295d696c822224334f0994f1f107df7efed72211d45a1118696f1427c84"},
- {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f51dcb39e910a853749250c0f82aced80bca3f7315e9c4ee14349eb7cab6a3f8"},
- {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8dddc52e0e486291be0ad39184da0607fae9cc665fdba1881211de9cfc0b332"},
- {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4863c49602722237e35cbce5aa91af4539cc63a671f59504d2b3f3767d898cf"},
- {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b1215facead57cc5325aef4229ef886e85d270b2ba02080fb5809ce9d2b81b4"},
- {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12049314497d872f6788f811e2b331654db207937f8a2fb34ff3e3cd9897faa"},
- {file = "reportlab-3.6.12-cp311-cp311-win32.whl", hash = "sha256:759495c2b8c15cb0d6b539c246896029e4cde42a896c3956f77e311c5f6b0807"},
- {file = "reportlab-3.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:666bdba4958b348460a765c48b8c0640e7085540846ed9494f47d8651604b33c"},
- {file = "reportlab-3.6.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a7c3369fa618eca79f9554ce06c618a5e738e592d61d96aa09b2457ca3ea410"},
- {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9b0861d8f40d7a24b094b8834f6a489b9e8c70bceaa7fa98237eed229671ce"},
- {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c25ea4afa8b92a2c14f4edc41c8fc30505745ce84cae86538e80cacadd7ae2"},
- {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55a070206580e161b6bbe1a96abf816c18d4c2c225d49916654714c93d842835"},
- {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c40e108072379ff83dd7442159ebc249d12eb8eec15b70614953fecd2c403792"},
- {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39e92fa4ab2a8f0f2cc051d9c1e3acb881340c07ef59c0c8b627861343d653c0"},
- {file = "reportlab-3.6.12-cp37-cp37m-win32.whl", hash = "sha256:3fd1ffdd5204301eb4c290a5752ac62f44d2d0b262e02e35a1e5234c13e14662"},
- {file = "reportlab-3.6.12-cp37-cp37m-win_amd64.whl", hash = "sha256:d4cecfb48a6cfbfe2caf0fc280cecea999699e63bc98cb02254bd87b39eff677"},
- {file = "reportlab-3.6.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b6a1b685da0b9a8000bb980e02d9d5be202d0cc539af113b661c76c051fca6f1"},
- {file = "reportlab-3.6.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f5808e1dac6b66c109d6205ce2aebf84bb89e1a1493b7e6df38932df5ebfb9cf"},
- {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb83df8f7840321d34cb5b24c972c617a8c1716c8a36e5050fff56adf5891b8c"},
- {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72ec333f089b4fce5a6d740ed0a1963a3994146be195722da0d8e14d4a7e1600"},
- {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71cf73f9907c444ef663ea653dbac24af07c307079572c3ff8f20ad1463af3b7"},
- {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cee3b6ebef5e4a8654ec5f0effeb1a2bb157ad87b0ac856871d25a805c0f2f90"},
- {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db62bed0774778fdf82c609cb9efd0062f2fdcd285be527d01f6be9fd9755888"},
- {file = "reportlab-3.6.12-cp38-cp38-win32.whl", hash = "sha256:b777ddc57b2d3366cbc540616034cdc1089ca0a31fefc907028e1dd62a6bf16c"},
- {file = "reportlab-3.6.12-cp38-cp38-win_amd64.whl", hash = "sha256:c07ec796a2a5d44bf787f2b623b6e668a389b0cafb78af34cf74554ff3bc532b"},
- {file = "reportlab-3.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cdd206883e999278d2af656f988dfcc89eb0c175ce6d75e87b713cf1e792c0c4"},
- {file = "reportlab-3.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a62e51a4a47616896bd0f1e9cc3fbfb174b713794a5031a34b84f69dbe01775"},
- {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dd0307b2b13b0482ac8314fd793fbbce263a428b189371addf0466784e1d597"},
- {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c56d701f7dc662e1d3d7fe364e66fa1339eafce54a488c2d16ec0ea49dc213c2"},
- {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:109009b02fc225882ea766a5ed8be0ef473fa1356e252a3f651a6aa89b4a195f"},
- {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b3648f3c340b6b6aabf9352341478c708cee6f00c5cd5c902311fcf4ce870f3c"},
- {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:907f7cd4832bb295d0c1573de15cc5aab5988282caf2ee7a2b1276fb6cdf502b"},
- {file = "reportlab-3.6.12-cp39-cp39-win32.whl", hash = "sha256:93e229519d046491b798f2c12dbbf2f3e237e89589aa5cbb5e1d8c1a978816db"},
- {file = "reportlab-3.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:498b4ec7e73426de64c6bf6ec03c5b3f10dedf5db8a9e13fdf195f95a3d065aa"},
- {file = "reportlab-3.6.12.tar.gz", hash = "sha256:b13cebf4e397bba14542bcd023338b6ff2c151a3a12aabca89eecbf972cb361a"},
+ {file = "reportlab-4.2.2-py3-none-any.whl", hash = "sha256:927616931637e2f13e2ee3b3b6316d7a07803170e258621cff7d138bde17fbb5"},
+ {file = "reportlab-4.2.2.tar.gz", hash = "sha256:765eecbdd68491c56947e29c38b8b69b834ee5dbbdd2fb7409f08ebdebf04428"},
]
[package.dependencies]
+chardet = "*"
pillow = ">=9.0.0"
[package.extras]
-fttextpath = ["freetype-py (>=2.3.0,<2.4)"]
-rlpycairo = ["rlPyCairo (>=0.1.0)"]
+accel = ["rl-accel (>=0.9.0,<1.1)"]
+pycairo = ["freetype-py (>=2.3.0,<2.4)", "rlPyCairo (>=0.2.0,<1)"]
+renderpm = ["rl-renderPM (>=4.0.3,<4.1)"]
[[package]]
name = "requests"
-version = "2.28.1"
+version = "2.32.3"
description = "Python HTTP for Humans."
-optional = true
-python-versions = ">=3.7, <4"
+optional = false
+python-versions = ">=3.8"
files = [
- {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
- {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
+ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
+ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
]
[package.dependencies]
certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<3"
+charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
-urllib3 = ">=1.21.1,<1.27"
+urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+[[package]]
+name = "ruff"
+version = "0.4.10"
+description = "An extremely fast Python linter and code formatter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"},
+ {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"},
+ {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"},
+ {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"},
+ {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"},
+ {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"},
+ {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"},
+ {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"},
+ {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"},
+ {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"},
+ {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"},
+]
+
[[package]]
name = "sentry-sdk"
-version = "1.21.0"
+version = "1.45.0"
description = "Python client for Sentry (https://sentry.io)"
optional = false
python-versions = "*"
files = [
- {file = "sentry-sdk-1.21.0.tar.gz", hash = "sha256:36a1ca082a3065a8a05aafa4b1e3d74e9459d41fbb0bea8a5364caca68626341"},
- {file = "sentry_sdk-1.21.0-py2.py3-none-any.whl", hash = "sha256:82faf9e2c9eb77401a7a187094b126ca25c2a3a478de6704612f48b3346f7a84"},
+ {file = "sentry-sdk-1.45.0.tar.gz", hash = "sha256:509aa9678c0512344ca886281766c2e538682f8acfa50fd8d405f8c417ad0625"},
+ {file = "sentry_sdk-1.45.0-py2.py3-none-any.whl", hash = "sha256:1ce29e30240cc289a027011103a8c83885b15ef2f316a60bcc7c5300afa144f1"},
]
[package.dependencies]
@@ -1225,18 +1488,24 @@ urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""}
[package.extras]
aiohttp = ["aiohttp (>=3.5)"]
arq = ["arq (>=0.23)"]
+asyncpg = ["asyncpg (>=0.23)"]
beam = ["apache-beam (>=2.12)"]
bottle = ["bottle (>=0.12.13)"]
celery = ["celery (>=3)"]
+celery-redbeat = ["celery-redbeat (>=2)"]
chalice = ["chalice (>=1.16.0)"]
+clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
django = ["django (>=1.8)"]
falcon = ["falcon (>=1.4)"]
fastapi = ["fastapi (>=0.79.0)"]
-flask = ["blinker (>=1.1)", "flask (>=0.11)"]
+flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"]
grpcio = ["grpcio (>=1.21.1)"]
httpx = ["httpx (>=0.16.0)"]
huey = ["huey (>=2)"]
+loguru = ["loguru (>=0.5)"]
+openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
+opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
pure-eval = ["asttokens", "executing", "pure-eval"]
pymongo = ["pymongo (>=3.1)"]
pyspark = ["pyspark (>=2.4.4)"]
@@ -1248,22 +1517,6 @@ starlette = ["starlette (>=0.19.1)"]
starlite = ["starlite (>=1.48)"]
tornado = ["tornado (>=5)"]
-[[package]]
-name = "setuptools"
-version = "65.6.3"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
- {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
-]
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
[[package]]
name = "six"
version = "1.16.0"
@@ -1279,7 +1532,7 @@ files = [
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
-optional = true
+optional = false
python-versions = "*"
files = [
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
@@ -1288,27 +1541,26 @@ files = [
[[package]]
name = "sphinx"
-version = "4.5.0"
+version = "5.3.0"
description = "Python documentation generator"
-optional = true
+optional = false
python-versions = ">=3.6"
files = [
- {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"},
- {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"},
+ {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"},
+ {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"},
]
[package.dependencies]
alabaster = ">=0.7,<0.8"
-babel = ">=1.3"
-colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
-docutils = ">=0.14,<0.18"
-imagesize = "*"
-importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
-Jinja2 = ">=2.3"
-packaging = "*"
-Pygments = ">=2.0"
+babel = ">=2.9"
+colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
+docutils = ">=0.14,<0.20"
+imagesize = ">=1.3"
+Jinja2 = ">=3.0"
+packaging = ">=21.0"
+Pygments = ">=2.12"
requests = ">=2.5.0"
-snowballstemmer = ">=1.1"
+snowballstemmer = ">=2.0"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = ">=2.0.0"
@@ -1318,14 +1570,14 @@ sphinxcontrib-serializinghtml = ">=1.1.5"
[package.extras]
docs = ["sphinxcontrib-websupport"]
-lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"]
-test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"]
+lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"]
+test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"]
[[package]]
name = "sphinx-copybutton"
version = "0.4.0"
description = "Add a copy button to each of your code cells."
-optional = true
+optional = false
python-versions = ">=3.6"
files = [
{file = "sphinx-copybutton-0.4.0.tar.gz", hash = "sha256:8daed13a87afd5013c3a9af3575cc4d5bec052075ccd3db243f895c07a689386"},
@@ -1341,72 +1593,90 @@ rtd = ["ipython", "sphinx", "sphinx-book-theme"]
[[package]]
name = "sphinx-rtd-theme"
-version = "1.1.1"
+version = "1.3.0"
description = "Read the Docs theme for Sphinx"
-optional = true
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
files = [
- {file = "sphinx_rtd_theme-1.1.1-py2.py3-none-any.whl", hash = "sha256:31faa07d3e97c8955637fc3f1423a5ab2c44b74b8cc558a51498c202ce5cbda7"},
- {file = "sphinx_rtd_theme-1.1.1.tar.gz", hash = "sha256:6146c845f1e1947b3c3dd4432c28998a1693ccc742b4f9ad7c63129f0757c103"},
+ {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"},
+ {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"},
]
[package.dependencies]
-docutils = "<0.18"
-sphinx = ">=1.6,<6"
+docutils = "<0.19"
+sphinx = ">=1.6,<8"
+sphinxcontrib-jquery = ">=4,<5"
[package.extras]
dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"]
[[package]]
name = "sphinxcontrib-applehelp"
-version = "1.0.3"
+version = "1.0.8"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
-optional = true
-python-versions = ">=3.8"
+optional = false
+python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib.applehelp-1.0.3-py3-none-any.whl", hash = "sha256:ba0f2a22e6eeada8da6428d0d520215ee8864253f32facf958cca81e426f661d"},
- {file = "sphinxcontrib.applehelp-1.0.3.tar.gz", hash = "sha256:83749f09f6ac843b8cb685277dbc818a8bf2d76cc19602699094fe9a74db529e"},
+ {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"},
+ {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
-version = "1.0.2"
-description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
-optional = true
-python-versions = ">=3.5"
+version = "1.0.6"
+description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
+optional = false
+python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
- {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
+ {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"},
+ {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
-version = "2.0.0"
+version = "2.0.5"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
-optional = true
-python-versions = ">=3.6"
+optional = false
+python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
- {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
+ {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"},
+ {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["html5lib", "pytest"]
+[[package]]
+name = "sphinxcontrib-jquery"
+version = "4.1"
+description = "Extension to include jQuery on newer Sphinx releases"
+optional = false
+python-versions = ">=2.7"
+files = [
+ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"},
+ {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"},
+]
+
+[package.dependencies]
+Sphinx = ">=1.8"
+
[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
-optional = true
+optional = false
python-versions = ">=3.5"
files = [
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
@@ -1418,49 +1688,69 @@ test = ["flake8", "mypy", "pytest"]
[[package]]
name = "sphinxcontrib-qthelp"
-version = "1.0.3"
-description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
-optional = true
-python-versions = ">=3.5"
+version = "1.0.7"
+description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
+optional = false
+python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
- {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
+ {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"},
+ {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
-version = "1.1.5"
-description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
-optional = true
-python-versions = ">=3.5"
+version = "1.1.10"
+description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
+optional = false
+python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
- {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
+ {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"},
+ {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"},
]
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sqlparse"
-version = "0.4.4"
+version = "0.5.0"
description = "A non-validating SQL parser."
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
files = [
- {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
- {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
+ {file = "sqlparse-0.5.0-py3-none-any.whl", hash = "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663"},
+ {file = "sqlparse-0.5.0.tar.gz", hash = "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93"},
]
[package.extras]
-dev = ["build", "flake8"]
+dev = ["build", "hatch"]
doc = ["sphinx"]
-test = ["pytest", "pytest-cov"]
+
+[[package]]
+name = "stack-data"
+version = "0.6.3"
+description = "Extract data from python stack frames and tracebacks for informative displays"
+optional = false
+python-versions = "*"
+files = [
+ {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
+ {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
+]
+
+[package.dependencies]
+asttokens = ">=2.1.0"
+executing = ">=1.2.0"
+pure-eval = "*"
+
+[package.extras]
+tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
[[package]]
name = "tomli"
@@ -1475,101 +1765,105 @@ files = [
[[package]]
name = "traitlets"
-version = "5.8.1"
+version = "5.14.3"
description = "Traitlets Python configuration system"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "traitlets-5.8.1-py3-none-any.whl", hash = "sha256:a1ca5df6414f8b5760f7c5f256e326ee21b581742114545b462b35ffe3f04861"},
- {file = "traitlets-5.8.1.tar.gz", hash = "sha256:32500888f5ff7bbf3b9267ea31748fa657aaf34d56d85e60f91dda7dc7f5785b"},
+ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
+ {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
]
[package.extras]
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
+test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
[[package]]
name = "typing-extensions"
-version = "4.4.0"
-description = "Backported and Experimental Type Hints for Python 3.7+"
+version = "4.12.2"
+description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
- {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
+ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
+]
+
+[[package]]
+name = "tzdata"
+version = "2024.1"
+description = "Provider of IANA time zone data"
+optional = false
+python-versions = ">=2"
+files = [
+ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
+ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
]
[[package]]
name = "urllib3"
-version = "1.26.13"
+version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+python-versions = ">=3.8"
files = [
- {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
- {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
+ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
+ {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
-secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+h2 = ["h2 (>=4,<5)"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "virtualenv"
+version = "20.26.3"
+description = "Virtual Python Environment builder"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"},
+ {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"},
+]
+
+[package.dependencies]
+distlib = ">=0.3.7,<1"
+filelock = ">=3.12.2,<4"
+platformdirs = ">=3.9.1,<5"
+
+[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)"]
+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)"]
[[package]]
name = "wcwidth"
-version = "0.2.5"
+version = "0.2.13"
description = "Measures the displayed width of unicode strings in a terminal"
optional = false
python-versions = "*"
files = [
- {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
- {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
-]
-
-[[package]]
-name = "xapian-bindings"
-version = "0.1.0"
-description = "Meta-package to build and install xapian-bindings extension."
-optional = false
-python-versions = "*"
-files = [
- {file = "xapian-bindings-0.1.0.tar.gz", hash = "sha256:f2b0396082ebf4f6681ab43d6d8fd1f63b6964b18c32c91236ed067c6f62ad14"},
+ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
+ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
]
[[package]]
name = "xapian-haystack"
-version = "3.0.1"
+version = "3.1.0"
description = "A Xapian backend for Haystack"
optional = false
python-versions = "*"
files = [
- {file = "xapian-haystack-3.0.1.tar.gz", hash = "sha256:a5c0e1262b95008df4dfeb58d093c654acee3f2b27ea3f7d366900895cdc70f9"},
+ {file = "xapian-haystack-3.1.0.tar.gz", hash = "sha256:9f9ab90bf450bf6699d164594d569243aafb6c9f0990a16855f55a1d16bc09c6"},
]
[package.dependencies]
-django = ">=2.2"
+django = ">=3.2"
django-haystack = ">=2.8.0"
-
-[[package]]
-name = "zipp"
-version = "3.11.0"
-description = "Backport of pathlib-compatible object wrapper for zip files"
-optional = true
-python-versions = ">=3.7"
-files = [
- {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"},
- {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"},
-]
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
-testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
-
-[extras]
-docs = ["Sphinx", "sphinx-copybutton", "sphinx-rtd-theme"]
-testing = ["coverage"]
+filelock = ">=3.4"
[metadata]
lock-version = "2.0"
-python-versions = "^3.8"
-content-hash = "62519616aff5a472dac3dd8071a6404b1ee8eab12a197af717a0520f7ded0331"
+python-versions = "^3.10,<3.12"
+content-hash = "c33378496709848054a8e4ecd1ebf74df12f15a1bb66ab61d2958e6a3c40f812"
diff --git a/pyproject.toml b/pyproject.toml
index cb1a6aaf..2808d848 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,6 +6,7 @@ authors = [
"Skia ",
"klmp200 ",
"Krophil ",
+ "Maréchal ",
"Och ",
"tleb ",
"Soldat ",
@@ -19,50 +20,62 @@ homepage = "https://ae.utbm.fr/"
license = "GPL-3.0-only"
[tool.poetry.dependencies]
-python = "^3.8"
-Django = "^3.2"
-Pillow = "^9.2"
+python = "^3.10,<3.12" # Version is held back by mistune
+Django = "^4.2.13"
+Pillow = "^10.4.0"
mistune = "^0.8.4"
-django-jinja = "^2.10"
-cryptography = "^40.0"
-pyOpenSSL = "^23.1.1"
+django-jinja = "^2.11"
+cryptography = "^42.0.8"
pytz = "^2021.1"
djangorestframework = "^3.13"
django-phonenumber-field = "^6.3"
phonenumbers = "^8.12"
django-ajax-selects = "^2.1.0"
-reportlab = "^3.6"
+reportlab = "^4.2"
django-haystack = "^3.2.1"
xapian-haystack = "^3.0.1"
-xapian-bindings = "^0.1.0"
libsass = "^0.22"
django-ordered-model = "^3.7"
django-simple-captcha = "^0.5.17"
python-dateutil = "^2.8.2"
-psycopg2-binary = "2.9.3"
+psycopg2-binary = "^2.9"
sentry-sdk = "^1.21.0"
-pygraphviz = "^1.9"
+pygraphviz = "^1.1"
Jinja2 = "^3.1"
django-countries = "^7.5.1"
dict2xml = "^1.7.3"
+Sphinx = "^5" # Needed for building xapian
+tomli = "^2.0.1"
-# Extra optional dependencies
-coverage = {version = "^5.5", optional = true}
-
-# Docs extra dependencies
-Sphinx = {version = "^4.2.0", optional = true}
-sphinx-rtd-theme = {version = "^1.0.0", optional = true}
-sphinx-copybutton = {version = "^0.4.0", optional = true}
-
-[tool.poetry.extras]
-testing = ["coverage"]
-docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
-
-[tool.poetry.dev-dependencies]
-freezegun = "^1.2.2" # used to test time-dependent code
+[tool.poetry.group.dev.dependencies]
django-debug-toolbar = "^4.0.0"
-ipython = "^7.28.0"
-black = "^23.3.0"
+ipython = "^8.26.0"
+pre-commit = "^3.7.1"
+ruff = "^0.4.10" # Version used in pipeline is controlled by pre-commit hooks in .pre-commit.config.yaml
+
+[tool.poetry.group.tests.dependencies]
+freezegun = "^1.2.2" # used to test time-dependent code
+pytest = "^8.2.2"
+pytest-cov = "^5.0.0"
+pytest-django = "^4.8.0"
+
+[tool.poetry.group.docs.dependencies]
+sphinx-rtd-theme = "^1.0.0"
+sphinx-copybutton = "^0.4.0"
+
+[tool.poetry.group.docs]
+optional = true
+
+[tool.xapian]
+version = "1.4.25"
+
+[tool.ruff.lint]
+select = ["I", "F401"]
+
+[tool.pytest.ini_options]
+DJANGO_SETTINGS_MODULE = "sith.settings"
+python_files = ["tests.py", "test_*.py", "*_tests.py"]
+markers = ["slow"]
[build-system]
requires = ["poetry-core>=1.0.0"]
diff --git a/rootplace/admin.py b/rootplace/admin.py
index 362a5c4f..5531f2a2 100644
--- a/rootplace/admin.py
+++ b/rootplace/admin.py
@@ -14,6 +14,4 @@
#
#
-from django.contrib import admin
-
# Register your models here.
diff --git a/rootplace/models.py b/rootplace/models.py
index 5672eba4..084dfa73 100644
--- a/rootplace/models.py
+++ b/rootplace/models.py
@@ -14,6 +14,4 @@
#
#
-from django.db import models
-
# Create your models here.
diff --git a/rootplace/tests.py b/rootplace/tests.py
index f1bb174f..5b9e2af1 100644
--- a/rootplace/tests.py
+++ b/rootplace/tests.py
@@ -13,16 +13,15 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
-from datetime import date, timedelta
+from datetime import timedelta
-from django.core.management import call_command
from django.test import TestCase
from django.urls import reverse
from django.utils.timezone import localtime, now
from club.models import Club
-from core.models import User, RealGroup
-from counter.models import Customer, Product, Selling, Counter, Refilling
+from core.models import RealGroup, User
+from counter.models import Counter, Customer, Product, Refilling, Selling
from subscription.models import Subscription
@@ -43,7 +42,7 @@ class MergeUserTest(TestCase):
)
def setUp(self) -> None:
- self.client.login(username="root", password="plop")
+ self.client.force_login(self.root)
def test_simple(self):
self.to_delete.first_name = "Biggus"
@@ -68,15 +67,15 @@ class MergeUserTest(TestCase):
self.to_keep = User.objects.get(pk=self.to_keep.pk)
# fields of to_delete should be assigned to to_keep
# if they were not set beforehand
- self.assertEqual("Biggus", self.to_keep.first_name)
- self.assertEqual("Dickus", self.to_keep.last_name)
- self.assertEqual("B'ian", self.to_keep.nick_name)
- self.assertEqual("Jerusalem", self.to_keep.address)
- self.assertEqual("Rome", self.to_keep.parent_address)
- self.assertEqual(3, self.to_keep.groups.count())
- groups = list(self.to_keep.groups.all())
- expected = [subscribers, mde_admin, sas_admin]
- self.assertCountEqual(groups, expected)
+ assert "Biggus" == self.to_keep.first_name
+ assert "Dickus" == self.to_keep.last_name
+ assert "B'ian" == self.to_keep.nick_name
+ assert "Jerusalem" == self.to_keep.address
+ assert "Rome" == self.to_keep.parent_address
+ assert self.to_keep.groups.count() == 3
+ groups = sorted(self.to_keep.groups.all(), key=lambda i: i.id)
+ expected = sorted([subscribers, mde_admin, sas_admin], key=lambda i: i.id)
+ assert groups == expected
def test_both_subscribers_and_with_account(self):
Customer(user=self.to_keep, account_id="11000l", amount=0).save()
@@ -115,7 +114,7 @@ class MergeUserTest(TestCase):
quantity=4,
payment_method="SITH_ACCOUNT",
).save()
- today = date.today()
+ today = localtime(now()).date()
# both subscriptions began last month and shall end in 5 months
Subscription(
member=self.to_keep,
@@ -139,9 +138,9 @@ class MergeUserTest(TestCase):
# to_delete had 20€ and bought 4 barbar
# total should be 10 - 4 + 20 - 8 = 18
self.assertAlmostEqual(18, self.to_keep.customer.amount, delta=0.0001)
- self.assertEqual(2, self.to_keep.customer.buyings.count())
- self.assertEqual(2, self.to_keep.customer.refillings.count())
- self.assertTrue(self.to_keep.is_subscribed)
+ assert self.to_keep.customer.buyings.count() == 2
+ assert self.to_keep.customer.refillings.count() == 2
+ assert self.to_keep.is_subscribed
# to_keep had 5 months of subscription remaining and received
# 5 more months from to_delete, so he should be subscribed for 10 months
self.assertAlmostEqual(
@@ -193,7 +192,7 @@ class MergeUserTest(TestCase):
self.assertRedirects(res, self.to_keep.get_absolute_url())
# to_delete had 20€ and bought 4 barbar worth 2€ each
# total should be 20 - 8 = 12
- self.assertTrue(hasattr(self.to_keep, "customer"))
+ assert hasattr(self.to_keep, "customer")
self.assertAlmostEqual(12, self.to_keep.customer.amount, delta=0.0001)
def test_delete_has_no_account(self):
@@ -221,5 +220,5 @@ class MergeUserTest(TestCase):
self.assertRedirects(res, self.to_keep.get_absolute_url())
# to_keep had 20€ and bought 4 barbar worth 2€ each
# total should be 20 - 8 = 12
- self.assertTrue(hasattr(self.to_keep, "customer"))
+ assert hasattr(self.to_keep, "customer")
self.assertAlmostEqual(12, self.to_keep.customer.amount, delta=0.0001)
diff --git a/rootplace/views.py b/rootplace/views.py
index fbb04e79..7a045b73 100644
--- a/rootplace/views.py
+++ b/rootplace/views.py
@@ -32,7 +32,7 @@ from django.utils.translation import gettext as _
from django.views.generic import ListView
from django.views.generic.edit import FormView
-from core.models import User, OperationLog, SithFile
+from core.models import OperationLog, SithFile, User
from core.views import CanEditPropMixin
from counter.models import Customer
from forum.models import ForumMessageMeta
diff --git a/sas/admin.py b/sas/admin.py
index d1001ef8..3f6c6f42 100644
--- a/sas/admin.py
+++ b/sas/admin.py
@@ -18,7 +18,6 @@ from django.contrib import admin
from sas.models import *
-
admin.site.register(Album)
# admin.site.register(Picture)
admin.site.register(PeoplePictureRelation)
diff --git a/sas/migrations/0001_initial.py b/sas/migrations/0001_initial.py
index 41ed4dc9..7ef55737 100644
--- a/sas/migrations/0001_initial.py
+++ b/sas/migrations/0001_initial.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
+from django.db import migrations
class Migration(migrations.Migration):
diff --git a/sas/migrations/0002_auto_20161119_1241.py b/sas/migrations/0002_auto_20161119_1241.py
index 21f44323..15ba12ad 100644
--- a/sas/migrations/0002_auto_20161119_1241.py
+++ b/sas/migrations/0002_auto_20161119_1241.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/sas/models.py b/sas/models.py
index e9fe4f97..3342facf 100644
--- a/sas/models.py
+++ b/sas/models.py
@@ -14,19 +14,19 @@
#
#
+import os
+from io import BytesIO
+
+from django.conf import settings
+from django.core.cache import cache
from django.db import models
from django.urls import reverse
-from django.core.cache import cache
-from django.conf import settings
-from django.utils.translation import gettext_lazy as _
from django.utils import timezone
-
+from django.utils.translation import gettext_lazy as _
from PIL import Image
-from io import BytesIO
-import os
from core.models import SithFile, User
-from core.utils import resize_image, exif_auto_rotate
+from core.utils import exif_auto_rotate, resize_image
class SASPictureManager(models.Manager):
diff --git a/sas/tests.py b/sas/tests.py
index 46a200c2..d888e761 100644
--- a/sas/tests.py
+++ b/sas/tests.py
@@ -14,6 +14,4 @@
#
#
-from django.test import TestCase
-
# Create your tests here.
diff --git a/sas/views.py b/sas/views.py
index ff51fe37..c2821bac 100644
--- a/sas/views.py
+++ b/sas/views.py
@@ -14,34 +14,31 @@
#
#
-from django.shortcuts import redirect
-from django.http import HttpResponse, Http404
-from django.urls import reverse_lazy, reverse
-from core.views.forms import SelectDate
-from django.views.generic import DetailView, TemplateView
-from django.views.generic.edit import UpdateView, FormMixin, FormView
-from django.utils.translation import gettext_lazy as _
-from django.conf import settings
-from django import forms
-from django.core.exceptions import PermissionDenied
-from django.core.paginator import Paginator, InvalidPage
-
from ajax_select import make_ajax_field
from ajax_select.fields import AutoCompleteSelectMultipleField
+from django import forms
+from django.conf import settings
+from django.core.exceptions import PermissionDenied
+from django.core.paginator import InvalidPage, Paginator
+from django.http import Http404, HttpResponse
+from django.shortcuts import redirect
+from django.urls import reverse, reverse_lazy
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, TemplateView
+from django.views.generic.edit import FormMixin, FormView, UpdateView
-from core.views import CanViewMixin, CanEditMixin
-from core.views.files import send_file, FileView
-from core.models import SithFile, User, Notification, RealGroup
-
-from sas.models import Picture, Album, PeoplePictureRelation
+from core.models import Notification, SithFile, User
+from core.views import CanEditMixin, CanViewMixin
+from core.views.files import FileView, MultipleImageField, send_file
+from core.views.forms import SelectDate
+from sas.models import Album, PeoplePictureRelation, Picture
class SASForm(forms.Form):
album_name = forms.CharField(
label=_("Add a new album"), max_length=30, required=False
)
- images = forms.ImageField(
- widget=forms.ClearableFileInput(attrs={"multiple": True}),
+ images = MultipleImageField(
label=_("Upload images"),
required=False,
)
diff --git a/sith/settings.py b/sith/settings.py
index 5ed279af..b8d0541d 100644
--- a/sith/settings.py
+++ b/sith/settings.py
@@ -57,6 +57,7 @@ SECRET_KEY = "(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
+TESTING = "pytest" in sys.modules
INTERNAL_IPS = ["127.0.0.1"]
ALLOWED_HOSTS = ["*"]
@@ -113,8 +114,6 @@ MIDDLEWARE = (
"core.middleware.SignalRequestMiddleware",
)
-TEST_RUNNER = "sith.testrunner.SithTestRunner"
-
ROOT_URLCONF = "sith.urls"
TEMPLATES = [
@@ -188,6 +187,7 @@ TEMPLATES = [
},
},
]
+FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"
HAYSTACK_CONNECTIONS = {
"default": {
@@ -249,8 +249,6 @@ TIME_ZONE = "Europe/Paris"
USE_I18N = True
-USE_L10N = True
-
USE_TZ = True
LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),)
@@ -567,7 +565,7 @@ SITH_SUBSCRIPTIONS = {
"name": _("One year for free(CA offer)"),
"price": 0,
"duration": 2,
- }
+ },
# To be completed....
}
@@ -691,14 +689,24 @@ if DEBUG:
"sith.toolbar_debug.TemplatesPanel",
"debug_toolbar.panels.cache.CachePanel",
"debug_toolbar.panels.signals.SignalsPanel",
- "debug_toolbar.panels.logging.LoggingPanel",
"debug_toolbar.panels.redirects.RedirectsPanel",
]
SASS_INCLUDE_FOLDERS = ["core/static/"]
SENTRY_ENV = "development"
-if "test" in sys.argv:
+if TESTING:
CAPTCHA_TEST_MODE = True
+ PASSWORD_HASHERS = [ # not secure, but faster password hasher
+ "django.contrib.auth.hashers.MD5PasswordHasher",
+ ]
+ STORAGES = { # store files in memory rather than using the hard drive
+ "default": {
+ "BACKEND": "django.core.files.storage.InMemoryStorage",
+ },
+ "staticfiles": {
+ "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
+ },
+ }
if SENTRY_DSN:
# Connection to sentry
diff --git a/sith/testrunner.py b/sith/testrunner.py
deleted file mode 100644
index 1112cf89..00000000
--- a/sith/testrunner.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.core.management import call_command
-from django.test.runner import DiscoverRunner
-
-
-class SithTestRunner(DiscoverRunner):
- def setup_databases(self, **kwargs):
- res = super().setup_databases(**kwargs)
- call_command("populate")
- return res
diff --git a/sith/urls.py b/sith/urls.py
index 6a098b5e..106c2851 100644
--- a/sith/urls.py
+++ b/sith/urls.py
@@ -29,14 +29,13 @@ Including another URLconf
1. Add an import: from blog import urls as blog_urls
2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
"""
-from django.urls import include, path
-from django.contrib import admin
+
+from ajax_select import urls as ajax_select_urls
from django.conf import settings
from django.conf.urls.static import static
+from django.contrib import admin
+from django.urls import include, path
from django.views.i18n import JavaScriptCatalog
-from ajax_select import urls as ajax_select_urls
-
-import core.urls
js_info_dict = {"packages": ("sith",)}
diff --git a/stock/admin.py b/stock/admin.py
index 46567a3b..28985c8c 100644
--- a/stock/admin.py
+++ b/stock/admin.py
@@ -25,7 +25,7 @@
from django.contrib import admin
-from stock.models import Stock, StockItem, ShoppingList, ShoppingListItem
+from stock.models import ShoppingList, ShoppingListItem, Stock, StockItem
# Register your models here.
admin.site.register(Stock)
diff --git a/stock/migrations/0001_initial.py b/stock/migrations/0001_initial.py
index 43b658ff..e203981a 100644
--- a/stock/migrations/0001_initial.py
+++ b/stock/migrations/0001_initial.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/stock/models.py b/stock/models.py
index 6c9c1ae7..f93a1544 100644
--- a/stock/models.py
+++ b/stock/models.py
@@ -23,11 +23,10 @@
#
#
-from django.db import models
-from django.utils.translation import gettext_lazy as _
-from django.urls import reverse
from django.conf import settings
-
+from django.db import models
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
from counter.models import Counter, ProductType
diff --git a/stock/tests.py b/stock/tests.py
index ad602a5c..884e1b09 100644
--- a/stock/tests.py
+++ b/stock/tests.py
@@ -23,6 +23,4 @@
#
#
-from django.test import TestCase
-
# Create your tests here.
diff --git a/stock/views.py b/stock/views.py
index 6b4776aa..72a9ab56 100644
--- a/stock/views.py
+++ b/stock/views.py
@@ -24,36 +24,21 @@
#
from collections import OrderedDict
-from datetime import datetime, timedelta
-from django.utils import timezone
-from django.shortcuts import render, get_object_or_404
-from django.views.generic import ListView, DetailView, RedirectView, TemplateView
-from django.views.generic.edit import (
- UpdateView,
- CreateView,
- DeleteView,
- ProcessFormView,
- FormMixin,
- BaseFormView,
-)
-from django.utils.translation import gettext_lazy as _
from django import forms
-from django.http import HttpResponseRedirect, HttpResponse
+from django.db import transaction
from django.forms.models import modelform_factory
-from django.urls import reverse_lazy, reverse
-from django.db import transaction, DataError
+from django.http import HttpResponseRedirect
+from django.urls import reverse, reverse_lazy
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, ListView
+from django.views.generic.edit import BaseFormView, CreateView, DeleteView, UpdateView
-from core.views import (
- CanViewMixin,
- CanEditMixin,
- CanEditPropMixin,
- CanCreateMixin,
- TabedViewMixin,
-)
+from core.views import CanCreateMixin, CanEditMixin, CanEditPropMixin, CanViewMixin
+from counter.models import ProductType
from counter.views import CounterAdminTabsMixin, CounterTabsMixin
-from counter.models import Counter, ProductType
-from stock.models import Stock, StockItem, ShoppingList, ShoppingListItem
+from stock.models import ShoppingList, ShoppingListItem, Stock, StockItem
class StockItemList(CounterAdminTabsMixin, CanCreateMixin, ListView):
diff --git a/subscription/migrations/0001_initial.py b/subscription/migrations/0001_initial.py
index 3ca942f0..932fafaf 100644
--- a/subscription/migrations/0001_initial.py
+++ b/subscription/migrations/0001_initial.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.contrib.auth.models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/subscription/models.py b/subscription/models.py
index f1c2b2d5..5e47ab75 100644
--- a/subscription/models.py
+++ b/subscription/models.py
@@ -14,17 +14,16 @@
#
#
+import math
from datetime import date, timedelta
-from django.db import models
-from django.utils.translation import gettext_lazy as _
-from django.conf import settings
-from django.core.exceptions import ValidationError
-from django.urls import reverse
-from django.contrib.auth.forms import PasswordResetForm
from dateutil.relativedelta import relativedelta
-
-import math
+from django.conf import settings
+from django.contrib.auth.forms import PasswordResetForm
+from django.core.exceptions import ValidationError
+from django.db import models
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
from core.models import User
from core.utils import get_start_of_semester
diff --git a/subscription/tests.py b/subscription/tests.py
index 4e8ec5a1..c347ab65 100644
--- a/subscription/tests.py
+++ b/subscription/tests.py
@@ -14,123 +14,102 @@
#
#
from datetime import date
-from unittest import mock
-from django.test import TestCase
-from subscription.models import Subscription
-from core.models import User
+import freezegun
+import pytest
from django.conf import settings
-from datetime import datetime
-from django.core.management import call_command
+from django.test import TestCase
+
+from core.models import User
+from subscription.models import Subscription
-class FakeDate(date):
- """A fake replacement for date that can be mocked for testing."""
-
- def __new__(cls, *args, **kwargs):
- return date.__new__(date, *args, **kwargs)
+@pytest.mark.parametrize(
+ ("today", "duration", "expected_start"),
+ [
+ (date(2020, 9, 18), 1, date(2020, 9, 18)),
+ (date(2020, 9, 18), 2, date(2020, 9, 18)),
+ (date(2020, 5, 17), 3, date(2020, 2, 15)),
+ (date(2021, 1, 18), 4, date(2020, 8, 15)),
+ (date(2020, 9, 18), 4, date(2020, 8, 15)),
+ ],
+)
+def test_subscription_compute_start_from_today(today, duration, expected_start):
+ with freezegun.freeze_time(today):
+ assert Subscription.compute_start(duration=duration) == expected_start
-def date_mock_today(year, month, day):
- FakeDate.today = classmethod(lambda cls: date(year, month, day))
+@pytest.mark.parametrize(
+ ("start_date", "duration", "expected_start"),
+ [
+ (date(2020, 5, 17), 1, date(2020, 5, 17)),
+ (date(2020, 5, 17), 2, date(2020, 5, 17)),
+ (date(2020, 5, 17), 3, date(2020, 2, 15)),
+ (date(2020, 1, 11), 3, date(2019, 8, 15)),
+ ],
+)
+def test_subscription_compute_start_explicit(start_date, duration, expected_start):
+ assert Subscription.compute_start(start_date, duration=duration) == expected_start
-class SubscriptionUnitTest(TestCase):
- @mock.patch("subscription.models.date", FakeDate)
- def test_start_dates_sliding_without_start(self):
- date_mock_today(2015, 9, 18)
- d = Subscription.compute_start(duration=1)
- self.assertTrue(d == date(2015, 9, 18))
- self.assertTrue(Subscription.compute_start(duration=2) == date(2015, 9, 18))
+@pytest.mark.parametrize(
+ ("today", "duration", "expected_end"),
+ [
+ (date(2020, 9, 18), 1, date(2021, 3, 18)),
+ (date(2020, 9, 18), 2, date(2021, 9, 18)),
+ (date(2020, 9, 18), 3, date(2022, 2, 15)),
+ (date(2020, 5, 17), 4, date(2022, 8, 15)),
+ (date(2020, 9, 18), 0.33, date(2020, 11, 18)),
+ (date(2020, 9, 18), 0.67, date(2021, 1, 19)),
+ (date(2020, 9, 18), 0.5, date(2020, 12, 18)),
+ ],
+)
+def test_subscription_compute_end_from_today(today, duration, expected_end):
+ with freezegun.freeze_time(today):
+ assert Subscription.compute_end(duration=duration) == expected_end
- def test_start_dates_sliding_with_start(self):
- self.assertTrue(
- Subscription.compute_start(date(2015, 5, 17), 1) == date(2015, 5, 17)
- )
- self.assertTrue(
- Subscription.compute_start(date(2015, 5, 17), 2) == date(2015, 5, 17)
- )
- @mock.patch("subscription.models.date", FakeDate)
- def test_start_dates_not_sliding_without_start(self):
- date_mock_today(2015, 5, 17)
- self.assertTrue(Subscription.compute_start(duration=3) == date(2015, 2, 15))
- date_mock_today(2016, 1, 18)
- self.assertTrue(Subscription.compute_start(duration=4) == date(2015, 8, 15))
- date_mock_today(2015, 9, 18)
- self.assertTrue(Subscription.compute_start(duration=4) == date(2015, 8, 15))
-
- def test_start_dates_not_sliding_with_start(self):
- self.assertTrue(
- Subscription.compute_start(date(2015, 5, 17), 3) == date(2015, 2, 15)
- )
- self.assertTrue(
- Subscription.compute_start(date(2015, 1, 11), 3) == date(2014, 8, 15)
- )
-
- @mock.patch("subscription.models.date", FakeDate)
- def test_end_dates_sliding(self):
- date_mock_today(2015, 9, 18)
- d = Subscription.compute_end(2)
- self.assertTrue(d == date(2016, 9, 18))
- d = Subscription.compute_end(1)
- self.assertTrue(d == date(2016, 3, 18))
-
- @mock.patch("subscription.models.date", FakeDate)
- def test_end_dates_not_sliding_without_start(self):
- date_mock_today(2015, 9, 18)
- d = Subscription.compute_end(duration=3)
- self.assertTrue(d == date(2017, 2, 15))
- d = Subscription.compute_end(duration=4)
- self.assertTrue(d == date(2017, 8, 15))
-
- @mock.patch("subscription.models.date", FakeDate)
- def test_end_dates_with_float(self):
- date_mock_today(2015, 9, 18)
- d = Subscription.compute_end(duration=0.33)
- self.assertTrue(d == date(2015, 11, 18))
- d = Subscription.compute_end(duration=0.67)
- self.assertTrue(d == date(2016, 1, 19))
- d = Subscription.compute_end(duration=0.5)
- self.assertTrue(d == date(2015, 12, 18))
-
- def test_end_dates_not_sliding_with_start(self):
- d = Subscription.compute_end(duration=3, start=date(2015, 9, 18))
- self.assertTrue(d == date(2017, 3, 18))
- d = Subscription.compute_end(duration=4, start=date(2015, 9, 18))
- self.assertTrue(d == date(2017, 9, 18))
+@pytest.mark.parametrize(
+ ("start_date", "duration", "expected_end"),
+ [
+ (date(2020, 9, 18), 3, date(2022, 3, 18)),
+ (date(2020, 9, 18), 4, date(2022, 9, 18)),
+ ],
+)
+def test_subscription_compute_end_from_today(start_date, duration, expected_end):
+ assert Subscription.compute_end(duration, start_date) == expected_end
class SubscriptionIntegrationTest(TestCase):
@classmethod
- def setUp(cls):
- cls.user = User.objects.filter(username="public").first()
+ def setUpTestData(cls):
+ cls.user = User.objects.get(username="public")
def test_duration_one_month(self):
s = Subscription(
- member=User.objects.filter(pk=self.user.pk).first(),
+ member=self.user,
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[3],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0],
)
s.subscription_start = date(2017, 8, 29)
s.subscription_end = s.compute_end(duration=0.166, start=s.subscription_start)
s.save()
- self.assertTrue(s.subscription_end == date(2017, 9, 29))
+ assert s.subscription_end == date(2017, 9, 29)
def test_duration_two_months(self):
s = Subscription(
- member=User.objects.filter(pk=self.user.pk).first(),
+ member=self.user,
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[3],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0],
)
s.subscription_start = date(2017, 8, 29)
s.subscription_end = s.compute_end(duration=0.333, start=s.subscription_start)
s.save()
- self.assertTrue(s.subscription_end == date(2017, 10, 29))
+ assert s.subscription_end == date(2017, 10, 29)
def test_duration_one_day(self):
s = Subscription(
- member=User.objects.filter(pk=self.user.pk).first(),
+ member=self.user,
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[3],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0],
)
@@ -140,44 +119,43 @@ class SubscriptionIntegrationTest(TestCase):
start=s.subscription_start,
)
s.save()
- self.assertTrue(s.subscription_end == date(2017, 8, 30))
+ assert s.subscription_end == date(2017, 8, 30)
def test_duration_three_months(self):
s = Subscription(
- member=User.objects.filter(pk=self.user.pk).first(),
+ member=self.user,
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[3],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0],
)
s.subscription_start = date(2017, 8, 29)
s.subscription_end = s.compute_end(duration=0.5, start=s.subscription_start)
s.save()
- self.assertTrue(s.subscription_end == date(2017, 11, 29))
+ assert s.subscription_end == date(2017, 11, 29)
def test_duration_four_months(self):
s = Subscription(
- member=User.objects.filter(pk=self.user.pk).first(),
+ member=self.user,
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[3],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0],
)
s.subscription_start = date(2017, 8, 29)
s.subscription_end = s.compute_end(duration=0.67, start=s.subscription_start)
s.save()
- self.assertTrue(s.subscription_end == date(2017, 12, 30))
+ assert s.subscription_end == date(2017, 12, 30)
def test_duration_six_weeks(self):
s = Subscription(
- member=User.objects.filter(pk=self.user.pk).first(),
+ member=self.user,
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[3],
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0],
)
s.subscription_start = date(2018, 9, 1)
s.subscription_end = s.compute_end(duration=0.23, start=s.subscription_start)
s.save()
- self.assertTrue(s.subscription_end == date(2018, 10, 13))
+ assert s.subscription_end == date(2018, 10, 13)
- @mock.patch("subscription.models.date", FakeDate)
def test_dates_sliding_with_subscribed_user(self):
- user = User.objects.filter(pk=self.user.pk).first()
+ user = self.user
s = Subscription(
member=user,
subscription_type="deux-semestres",
@@ -189,18 +167,16 @@ class SubscriptionIntegrationTest(TestCase):
start=s.subscription_start,
)
s.save()
- self.assertTrue(s.subscription_end == date(2016, 8, 29))
- date_mock_today(2016, 8, 25)
- d = Subscription.compute_end(
- duration=settings.SITH_SUBSCRIPTIONS["deux-semestres"]["duration"],
- user=user,
- )
+ assert s.subscription_end == date(2016, 8, 29)
+ with freezegun.freeze_time("2016-08-25"):
+ d = Subscription.compute_end(
+ duration=settings.SITH_SUBSCRIPTIONS["deux-semestres"]["duration"],
+ user=user,
+ )
+ assert d == date(2017, 8, 29)
- self.assertTrue(d == date(2017, 8, 29))
-
- @mock.patch("subscription.models.date", FakeDate)
def test_dates_renewal_sliding_during_two_free_monthes(self):
- user = User.objects.filter(pk=self.user.pk).first()
+ user = self.user
s = Subscription(
member=user,
subscription_type="deux-mois-essai",
@@ -212,17 +188,16 @@ class SubscriptionIntegrationTest(TestCase):
start=s.subscription_start,
)
s.save()
- self.assertTrue(s.subscription_end == date(2015, 10, 29))
- date_mock_today(2015, 9, 25)
- d = Subscription.compute_end(
- duration=settings.SITH_SUBSCRIPTIONS["deux-semestres"]["duration"],
- user=user,
- )
- self.assertTrue(d == date(2016, 10, 29))
+ assert s.subscription_end == date(2015, 10, 29)
+ with freezegun.freeze_time("2015-09-25"):
+ d = Subscription.compute_end(
+ duration=settings.SITH_SUBSCRIPTIONS["deux-semestres"]["duration"],
+ user=user,
+ )
+ assert d == date(2016, 10, 29)
- @mock.patch("subscription.models.date", FakeDate)
def test_dates_renewal_sliding_after_two_free_monthes(self):
- user = User.objects.filter(pk=self.user.pk).first()
+ user = self.user
s = Subscription(
member=user,
subscription_type="deux-mois-essai",
@@ -234,10 +209,10 @@ class SubscriptionIntegrationTest(TestCase):
start=s.subscription_start,
)
s.save()
- self.assertTrue(s.subscription_end == date(2015, 10, 29))
- date_mock_today(2015, 11, 5)
- d = Subscription.compute_end(
- duration=settings.SITH_SUBSCRIPTIONS["deux-semestres"]["duration"],
- user=user,
- )
- self.assertTrue(d == date(2016, 11, 5))
+ assert s.subscription_end == date(2015, 10, 29)
+ with freezegun.freeze_time("2015-11-05"):
+ d = Subscription.compute_end(
+ duration=settings.SITH_SUBSCRIPTIONS["deux-semestres"]["duration"],
+ user=user,
+ )
+ assert d == date(2016, 11, 5)
diff --git a/subscription/views.py b/subscription/views.py
index bd10ac2c..ccffd58f 100644
--- a/subscription/views.py
+++ b/subscription/views.py
@@ -14,21 +14,19 @@
#
#
-from django.views.generic.edit import CreateView, FormView
-from django.utils.translation import gettext_lazy as _
-from django.core.exceptions import PermissionDenied, ValidationError
-from django.urls import reverse_lazy
-from django import forms
-from django.conf import settings
-
-from ajax_select.fields import AutoCompleteSelectField
import random
-from subscription.models import Subscription
-from core.views.forms import SelectDateTime
+from ajax_select.fields import AutoCompleteSelectField
+from django import forms
+from django.conf import settings
+from django.core.exceptions import PermissionDenied, ValidationError
+from django.urls import reverse_lazy
+from django.utils.translation import gettext_lazy as _
+from django.views.generic.edit import CreateView, FormView
+
from core.models import User
-from core.views.forms import SelectDate
-from core.views.forms import TzAwareDateTimeField
+from core.views.forms import SelectDate, TzAwareDateTimeField
+from subscription.models import Subscription
class SelectionDateForm(forms.Form):
diff --git a/trombi/migrations/0001_initial.py b/trombi/migrations/0001_initial.py
index 0943bad1..3c609c63 100644
--- a/trombi/migrations/0001_initial.py
+++ b/trombi/migrations/0001_initial.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
-from django.conf import settings
import datetime
+
import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/trombi/migrations/0004_trombiclubmembership.py b/trombi/migrations/0004_trombiclubmembership.py
index 5bb017b4..eb036839 100644
--- a/trombi/migrations/0004_trombiclubmembership.py
+++ b/trombi/migrations/0004_trombiclubmembership.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/trombi/models.py b/trombi/models.py
index 18f36526..b5e7d1a8 100644
--- a/trombi/models.py
+++ b/trombi/models.py
@@ -22,17 +22,17 @@
#
#
-from django.db import models
-from django.utils.translation import gettext_lazy as _
-from django.urls import reverse
+from datetime import date
+
from django.conf import settings
from django.core.exceptions import ValidationError
+from django.db import models
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
-from datetime import timedelta, date
-
-from core.models import User
-from core.utils import get_start_of_semester, get_semester_code
from club.models import Club
+from core.models import User
+from core.utils import get_semester_code
class TrombiManager(models.Manager):
diff --git a/trombi/tests.py b/trombi/tests.py
index de62cd38..087c75b4 100644
--- a/trombi/tests.py
+++ b/trombi/tests.py
@@ -22,6 +22,4 @@
#
#
-from django.test import TestCase
-
# Create your tests here.
diff --git a/trombi/views.py b/trombi/views.py
index 98bf5fc2..d2bf3231 100644
--- a/trombi/views.py
+++ b/trombi/views.py
@@ -23,34 +23,33 @@
#
#
-from django.http import Http404, HttpResponseRedirect
-from django.shortcuts import get_object_or_404, redirect
-from django.urls import reverse_lazy, reverse
-from django.views.generic import DetailView, RedirectView, TemplateView, View
-from django.views.generic.edit import UpdateView, CreateView, DeleteView
-from django.utils.translation import gettext_lazy as _
-from django import forms
-from django.conf import settings
-from django.forms.models import modelform_factory
-from django.core.exceptions import PermissionDenied
-
-from ajax_select.fields import AutoCompleteSelectField
-
from datetime import date
-from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
-from core.views.forms import SelectDate
+from ajax_select.fields import AutoCompleteSelectField
+from django import forms
+from django.conf import settings
+from django.core.exceptions import PermissionDenied
+from django.forms.models import modelform_factory
+from django.http import Http404, HttpResponseRedirect
+from django.shortcuts import get_object_or_404, redirect
+from django.urls import reverse, reverse_lazy
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, RedirectView, TemplateView, View
+from django.views.generic.edit import CreateView, DeleteView, UpdateView
+
+from club.models import Club
+from core.models import User
from core.views import (
- CanViewMixin,
+ CanCreateMixin,
CanEditMixin,
CanEditPropMixin,
- TabedViewMixin,
- CanCreateMixin,
+ CanViewMixin,
QuickNotifMixin,
+ TabedViewMixin,
UserIsLoggedMixin,
)
-from core.models import User
-from club.models import Club
+from core.views.forms import SelectDate
+from trombi.models import Trombi, TrombiClubMembership, TrombiComment, TrombiUser
class TrombiTabsMixin(TabedViewMixin):