diff --git a/aleksis/core/admin.py b/aleksis/core/admin.py index 0725c0aa5545797317a3bfac955cd854d28f227e..e3258ed33bea0fc4dbcb452c2f2f57573212dbe2 100644 --- a/aleksis/core/admin.py +++ b/aleksis/core/admin.py @@ -1,6 +1,15 @@ from django.contrib import admin -from .models import Group, Person, School, SchoolTerm, Activity, Notification +from .models import ( + Group, + Person, + School, + SchoolTerm, + Activity, + Notification, + Announcement, + AnnouncementRecipient, +) admin.site.register(Person) admin.site.register(Group) @@ -8,3 +17,16 @@ admin.site.register(School) admin.site.register(SchoolTerm) admin.site.register(Activity) admin.site.register(Notification) + + +class AnnouncementRecipientInline(admin.StackedInline): + model = AnnouncementRecipient + + +class AnnouncementAdmin(admin.ModelAdmin): + inlines = [ + AnnouncementRecipientInline, + ] + + +admin.site.register(Announcement, AnnouncementAdmin) diff --git a/aleksis/core/checks.py b/aleksis/core/checks.py index 91c5e6291ad44d468d1f08b855688d1485f80390..e52290eb3a48ae4d7e7efe90fbec61d8f86ed365 100644 --- a/aleksis/core/checks.py +++ b/aleksis/core/checks.py @@ -3,13 +3,14 @@ from typing import Optional import django.apps from django.core.checks import Tags, Warning, register +from .mixins import ExtensibleModel, PureDjangoModel from .util.apps import AppConfig @register(Tags.compatibility) def check_app_configs_base_class( app_configs: Optional[django.apps.registry.Apps] = None, **kwargs -) -> None: +) -> list: """ Checks whether all apps derive from AlekSIS's base app config """ results = [] @@ -17,11 +18,11 @@ def check_app_configs_base_class( if app_configs is None: app_configs = django.apps.apps.get_app_configs() - for app_config in app_configs: - if app_config.name.startswith("aleksis.apps.") and not isinstance(app_config, AppConfig): + for app_config in filter(lambda c: c.name.startswith("aleksis."), app_configs): + if not isinstance(app_config, AppConfig): results.append( Warning( - "App config %s does not derive from aleksis.core.util.apps.AppConfig.", + "App config %s does not derive from aleksis.core.util.apps.AppConfig." % app_config.name, hint="Ensure the app uses the correct base class for all registry functionality to work.", obj=app_config, id="aleksis.core.W001", @@ -29,3 +30,29 @@ def check_app_configs_base_class( ) return results + + +@register(Tags.compatibility) +def check_app_models_base_class( + app_configs: Optional[django.apps.registry.Apps] = None, **kwargs +) -> list: + """ Checks whether all app models derive from AlekSIS's base ExtensibleModel """ + + results = [] + + if app_configs is None: + app_configs = django.apps.apps.get_app_configs() + + for app_config in filter(lambda c: c.name.startswith("aleksis."), app_configs): + for model in app_config.get_models(): + if ExtensibleModel not in model.__mro__ and PureDjangoModel not in model.__mro__: + results.append( + Warning( + "Model %s in app config %s does not derive from aleksis.core.mixins.ExtensibleModel." % (model._meta.object_name, app_config.name), + hint="Ensure all models in AlekSIS use ExtensibleModel as base. If your deviation is intentional, you can add the PureDjangoModel mixin instead to silence this warning.", + obj=model, + id="aleksis.core.W002", + ) + ) + + return results diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index 65d0a584858322665bbfad34af67aebd539f6b4f..1c41dbb704847658c4ae91e4e060ff54bc1548b0 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -9,6 +9,12 @@ MENUS = { "icon": "lock_open", "validators": ["menu_generator.validators.is_anonymous"], }, + { + "name": _("Dashboard"), + "url": "index", + "icon": "home", + "validators": ["menu_generator.validators.is_authenticated"], + }, { "name": _("Account"), "url": "#", diff --git a/aleksis/core/migrations/0013_extensible_model_as_default.py b/aleksis/core/migrations/0013_extensible_model_as_default.py new file mode 100644 index 0000000000000000000000000000000000000000..4e23d86f49c0fcc53d92813b794c4083e475a6e3 --- /dev/null +++ b/aleksis/core/migrations/0013_extensible_model_as_default.py @@ -0,0 +1,38 @@ +# Generated by Django 3.0.3 on 2020-02-20 12:24 + +import aleksis.core.util.core_helpers +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0012_announcement'), + ] + + operations = [ + migrations.RemoveField( + model_name='activity', + name='created_at', + ), + migrations.RemoveField( + model_name='notification', + name='created_at', + ), + migrations.AddField( + model_name='activity', + name='extended_data', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False), + ), + migrations.AddField( + model_name='announcement', + name='extended_data', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False), + ), + migrations.AddField( + model_name='notification', + name='extended_data', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False), + ), + ] diff --git a/aleksis/core/migrations/0014_multiple_recipients_announcement.py b/aleksis/core/migrations/0014_multiple_recipients_announcement.py new file mode 100644 index 0000000000000000000000000000000000000000..ebe1aa81d3791b80b93d5afa2a2309cb0f09ca2c --- /dev/null +++ b/aleksis/core/migrations/0014_multiple_recipients_announcement.py @@ -0,0 +1,51 @@ +# Generated by Django 3.0.3 on 2020-02-19 18:14 + +import aleksis.core.models +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('core', '0013_extensible_model_as_default'), + ] + + operations = [ + migrations.AlterModelOptions( + name='announcement', + options={'verbose_name': 'Announcement', 'verbose_name_plural': 'Announcements'}, + ), + migrations.RemoveField( + model_name='announcement', + name='content_type', + ), + migrations.RemoveField( + model_name='announcement', + name='recipient_id', + ), + migrations.AlterField( + model_name='announcement', + name='description', + field=models.TextField(blank=True, max_length=500, verbose_name='Description'), + ), + migrations.AlterField( + model_name='announcement', + name='valid_until', + field=models.DateTimeField(default=aleksis.core.util.core_helpers.now_tomorrow, verbose_name='Date and time until when to show'), + ), + migrations.CreateModel( + name='AnnouncementRecipient', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recipient_id', models.PositiveIntegerField()), + ('announcement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recipients', to='core.Announcement')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + migrations.AlterModelOptions( + name='announcementrecipient', + options={'verbose_name': 'Announcement recipient', 'verbose_name_plural': 'Announcement recipients'}, + ), + ] diff --git a/aleksis/core/migrations/0015_announcement_recipient_extensible_model.py b/aleksis/core/migrations/0015_announcement_recipient_extensible_model.py new file mode 100644 index 0000000000000000000000000000000000000000..2faf84a8cef450c44d737942cfebcfc95695267f --- /dev/null +++ b/aleksis/core/migrations/0015_announcement_recipient_extensible_model.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2020-02-25 15:26 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0014_multiple_recipients_announcement'), + ] + + operations = [ + migrations.AddField( + model_name='announcementrecipient', + name='extended_data', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False), + ), + ] diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py index ff79940ea9e5e091b381a99a85b2d1f8a9587d1a..984a412556b69a71c5ce875f927989263ffce228 100644 --- a/aleksis/core/mixins.py +++ b/aleksis/core/mixins.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Any, Callable, Optional from django.contrib.contenttypes.models import ContentType @@ -8,9 +9,34 @@ from easyaudit.models import CRUDEvent from jsonstore.fields import JSONField, JSONFieldMixin -class ExtensibleModel(models.Model): - """ Allow injection of fields and code from AlekSIS apps to extend - model functionality. +class CRUDMixin(models.Model): + class Meta: + abstract = True + + @property + def crud_events(self) -> QuerySet: + """Get all CRUD events connected to this object from easyaudit.""" + + content_type = ContentType.objects.get_for_model(self) + + return CRUDEvent.objects.filter( + object_id=self.pk, content_type=content_type + ).select_related("user") + + +class ExtensibleModel(CRUDMixin): + """ Base model for all objects in AlekSIS apps + + This base model ensures all objects in AlekSIS apps fulfill the + following properties: + + * crud_events property to retrieve easyaudit's CRUD event log + * created_at and updated_at properties based n CRUD events + * Allow injection of fields and code from AlekSIS apps to extend + model functionality. + + Injection of fields and code + ============================ After all apps have been loaded, the code in the `model_extensions` module in every app is executed. All code that shall be injected into a model goes there. @@ -43,8 +69,48 @@ class ExtensibleModel(models.Model): - Dominik George <dominik.george@teckids.org> """ + @property + def crud_event_create(self) -> Optional[CRUDEvent]: + """ Return create event of this object """ + return self.crud_events.filter(event_type=CRUDEvent.CREATE).latest("datetime") + + @property + def crud_event_update(self) -> Optional[CRUDEvent]: + """ Return last event of this object """ + return self.crud_events.latest("datetime") + + @property + def created_at(self) -> Optional[datetime]: + """ Determine creation timestamp from CRUD log """ + + if self.crud_event_create: + return self.crud_event_create.datetime + + @property + def updated_at(self) -> Optional[datetime]: + """ Determine last timestamp from CRUD log """ + + if self.crud_event_update: + return self.crud_event_update.datetime + extended_data = JSONField(default=dict, editable=False) - + + @property + def created_by(self) -> Optional[models.Model]: + """ Determine user who created this object from CRUD log """ + + if self.crud_event_create: + return self.crud_event_create.user + + @property + def updated_by(self) -> Optional[models.Model]: + """ Determine user who last updated this object from CRUD log """ + + if self.crud_event_update: + return self.crud_event_update.user + + extended_data = JSONField(default=dict, editable=False) + @classmethod def _safe_add(cls, obj: Any, name: Optional[str]) -> None: # Decide the name for the attribute @@ -101,17 +167,6 @@ class ExtensibleModel(models.Model): class Meta: abstract = True - -class CRUDMixin(models.Model): - class Meta: - abstract = True - - @property - def crud_events(self) -> QuerySet: - """Get all CRUD events connected to this object from easyaudit.""" - - content_type = ContentType.objects.get_for_model(self) - - return CRUDEvent.objects.filter( - object_id=self.pk, content_type=content_type - ).select_related("user") +class PureDjangoModel(object): + """ No-op mixin to mark a model as deliberately not using ExtensibleModel """ + pass diff --git a/aleksis/core/models.py b/aleksis/core/models.py index ad40d171e660cd7a666edb7f4322d4de98dbed58..9f001cc714584c9c4694d44b5f53f72971f3f6b9 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -1,5 +1,5 @@ from datetime import date, datetime -from typing import Optional, Iterable, Union, Sequence +from typing import Optional, Iterable, Union, Sequence, List from django.contrib.auth import get_user_model from django.contrib.auth.models import User @@ -8,12 +8,14 @@ from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models import QuerySet from django.forms.widgets import Media +from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from image_cropping import ImageCropField, ImageRatioField from phonenumber_field.modelfields import PhoneNumberField from polymorphic.models import PolymorphicModel -from .mixins import ExtensibleModel +from .mixins import ExtensibleModel, PureDjangoModel +from .util.core_helpers import now_tomorrow from .util.notifications import send_notification from constance import config @@ -229,13 +231,13 @@ class Group(ExtensibleModel): @property def announcement_recipients(self): - return list(self.members) + list(self.owners) + return list(self.members.all()) + list(self.owners.all()) def __str__(self) -> str: return "%s (%s)" % (self.name, self.short_name) -class Activity(models.Model): +class Activity(ExtensibleModel): user = models.ForeignKey("Person", on_delete=models.CASCADE, related_name="activities") title = models.CharField(max_length=150, verbose_name=_("Title")) @@ -243,8 +245,6 @@ class Activity(models.Model): app = models.CharField(max_length=100, verbose_name=_("Application")) - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at")) - def __str__(self): return self.title @@ -253,7 +253,7 @@ class Activity(models.Model): verbose_name_plural = _("Activities") -class Notification(models.Model): +class Notification(ExtensibleModel): sender = models.CharField(max_length=100, verbose_name=_("Sender")) recipient = models.ForeignKey("Person", on_delete=models.CASCADE, related_name="notifications") @@ -264,8 +264,6 @@ class Notification(models.Model): read = models.BooleanField(default=False, verbose_name=_("Read")) sent = models.BooleanField(default=False, verbose_name=_("Sent")) - created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at")) - def __str__(self): return self.title @@ -279,17 +277,18 @@ class Notification(models.Model): verbose_name_plural = _("Notifications") -class Announcement(models.Model): +class Announcement(ExtensibleModel): title = models.CharField(max_length=150, verbose_name=_("Title")) - description = models.TextField(max_length=500, verbose_name=_("Description")) + description = models.TextField(max_length=500, verbose_name=_("Description"), blank=True) link = models.URLField(blank=True, verbose_name=_("Link")) - valid_from = models.DateTimeField(verbose_name=_("Date and time from when to show"), default=datetime.now) - valid_until = models.DateTimeField(verbose_name=_("Date and time until when to show"), null=True, blank=True) - - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - recipient_id = models.PositiveIntegerField() - recipient = GenericForeignKey("content_type", "recipient_id") + valid_from = models.DateTimeField( + verbose_name=_("Date and time from when to show"), default=timezone.datetime.now + ) + valid_until = models.DateTimeField( + verbose_name=_("Date and time until when to show"), + default=now_tomorrow, + ) @classmethod def relevant_for(cls, obj: Union[models.Model, models.QuerySet]) -> models.QuerySet: @@ -304,13 +303,53 @@ class Announcement(models.Model): ct = ContentType.objects.get_for_model(obj) pks = [obj.pk] - return cls.objects.filter(content_type=ct, recipient_id__in=pks) + return cls.objects.filter(recipients__content_type=ct, recipients__recipient_id__in=pks) + + @classmethod + def for_person_at_time(cls, person: Person, when: Optional[datetime] = None) -> List: + """ Get all announcements for one person at a certain time """ + when = when or timezone.datetime.now() + + # Get announcements by time + announcements = cls.objects.filter(valid_from__lte=when, valid_until__gte=when) + + # Filter by person + announcements_for_person = [] + for announcement in announcements: + if person in announcement.recipient_persons: + announcements_for_person.append(announcement) + + return announcements_for_person + + @property + def recipient_persons(self) -> Sequence[Person]: + """ Return a list of Persons this announcement is relevant for """ + + persons = [] + for recipient in self.recipients.all(): + persons += recipient.persons + return persons + + def __str__(self): + return self.title + + class Meta: + verbose_name = _("Announcement") + verbose_name_plural = _("Announcements") + + +class AnnouncementRecipient(ExtensibleModel): + announcement = models.ForeignKey(Announcement, on_delete=models.CASCADE, related_name="recipients") + + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + recipient_id = models.PositiveIntegerField() + recipient = GenericForeignKey("content_type", "recipient_id") @property - def recipient_persons(self) -> Union[models.QuerySet, Sequence[models.Model]]: - """ Return a list of Persons this announcement is relevant for + def persons(self) -> Sequence[Person]: + """ Return a list of Persons selected by this recipient object - If the recipient is a Person, return that object. If not, it returns the QUerySet + If the recipient is a Person, return that object. If not, it returns the list from the announcement_recipients field on the target model. """ @@ -319,14 +358,15 @@ class Announcement(models.Model): else: return getattr(self.recipient, "announcement_recipients", []) - def save(self, **kwargs): - if not self.valid_until: - self.valid_until = self.valid_from + def __str__(self): + return str(self.recipient) - super().save(**kwargs) + class Meta: + verbose_name = _("Announcement recipient") + verbose_name_plural = _("Announcement recipients") -class DashboardWidget(PolymorphicModel): +class DashboardWidget(PolymorphicModel, PureDjangoModel): """ Base class for dashboard widgets on the index page To implement a widget, add a model that subclasses DashboardWidget, sets the template diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index fa098c1fc62a32f13c55aeaeccd461e1445fde52..f1cee79a5befae2cf9b49ea2c16e6d8a396c320c 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -50,6 +50,7 @@ INSTALLED_APPS = [ "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", + "django.contrib.sites", "django.contrib.staticfiles", "django.contrib.humanize", "polymorphic", @@ -458,6 +459,8 @@ PWA_APP_SPLASH_SCREEN = [ ] PWA_SERVICE_WORKER_PATH = os.path.join(STATIC_ROOT, "js", "serviceworker.js") +SITE_ID = 1 + CKEDITOR_CONFIGS = { 'default': { 'toolbar_Basic': [ diff --git a/aleksis/core/templates/core/announcements.html b/aleksis/core/templates/core/announcements.html new file mode 100644 index 0000000000000000000000000000000000000000..3e1925536a12bb3824a5b5bb11adb636fcfc9bf2 --- /dev/null +++ b/aleksis/core/templates/core/announcements.html @@ -0,0 +1,41 @@ +{% load i18n humanize %} + +{% for announcement in announcements %} + <div class="alert primary"> + <div> + {% if show_interval %} + <em class="right hide-on-small-and-down"> + {% if announcement.valid_from.date == announcement.valid_until.date %} + {% blocktrans with from=announcement.valid_from|naturalday %} + Valid for {{ from }} + {% endblocktrans %} + {% else %} + {% blocktrans with from=announcement.valid_from|naturalday until=announcement.valid_until|naturalday %} + Valid for {{ from }} – {{ until }} + {% endblocktrans %} + {% endif %} + </em> + {% endif %} + + <i class="material-icons left">announcement</i> + <p> + <strong>{{ announcement.title }}</strong> <br/> + {{ announcement.description }} + </p> + + {% if show_interval %} + <em class="hide-on-med-and-up"> + {% if announcement.valid_from.date == announcement.valid_until.date %} + {% blocktrans with from=announcement.valid_from|naturalday %} + Valid for {{ from }} + {% endblocktrans %} + {% else %} + {% blocktrans with from=announcement.valid_from|naturalday until=announcement.valid_until|naturalday %} + Valid for {{ from }} – {{ until }} + {% endblocktrans %} + {% endif %} + </em> + {% endif %} + </div> + </div> +{% endfor %} diff --git a/aleksis/core/templates/core/index.html b/aleksis/core/templates/core/index.html index 9e8e19ac71dd20e17ca690c560a81767975e85aa..2ace14e98652ded4e56c840c180b10690b1fb7d6 100644 --- a/aleksis/core/templates/core/index.html +++ b/aleksis/core/templates/core/index.html @@ -28,6 +28,8 @@ </div> {% endfor %} + {% include "core/announcements.html" with announcements=announcements %} + <div class="row" id="live_load"> {% for widget in widgets %} <div class="col s12 m12 l6 xl4"> diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index f273016659cf08bf7d4e924296d2950dc9e20694..9d019103d2e4b45558a5119c05b26a618d24be87 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -1,3 +1,4 @@ +from datetime import datetime, timedelta import os import pkgutil from importlib import import_module @@ -7,6 +8,7 @@ from uuid import uuid4 from django.conf import settings from django.db.models import Model from django.http import HttpRequest +from django.utils import timezone from django.utils.functional import lazy @@ -148,3 +150,8 @@ def school_information_processor(request: HttpRequest) -> dict: return { "SCHOOL": School.get_default, } + + +def now_tomorrow() -> datetime: + """ Return current time tomorrow """ + return timezone.datetime.now() + timedelta(days=1) diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 2e1946de401b866e0adbc4aa3ee8463b5f1b94af..98db80da4a5d30df58f6bc2a8d3d037f226a0044 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -16,7 +16,7 @@ from .forms import ( EditTermForm, PersonsAccountsFormSet, ) -from .models import Activity, Group, Notification, Person, School, DashboardWidget +from .models import Activity, Group, Notification, Person, School, DashboardWidget, Announcement from .tables import GroupsTable, PersonsTable from .util import messages @@ -33,6 +33,9 @@ def index(request: HttpRequest) -> HttpResponse: context["notifications"] = notifications context["unread_notifications"] = unread_notifications + announcements = Announcement.for_person_at_time(request.user.person) + context["announcements"] = announcements + widgets = DashboardWidget.objects.filter(active=True) media = DashboardWidget.get_media(widgets) diff --git a/apps/official/AlekSIS-App-Alsijil b/apps/official/AlekSIS-App-Alsijil index f04cda54c04eba8f5430c6f562e39a954deb41cb..bd516e09a6e651b7171a6d7b31f79c3e4a22892e 160000 --- a/apps/official/AlekSIS-App-Alsijil +++ b/apps/official/AlekSIS-App-Alsijil @@ -1 +1 @@ -Subproject commit f04cda54c04eba8f5430c6f562e39a954deb41cb +Subproject commit bd516e09a6e651b7171a6d7b31f79c3e4a22892e diff --git a/apps/official/AlekSIS-App-Chronos b/apps/official/AlekSIS-App-Chronos index 182b37deddc5049fb37c3b0a65ad0516c6147e7b..455c9d1bb74ae0d727b654a54d8f7c0278308a4e 160000 --- a/apps/official/AlekSIS-App-Chronos +++ b/apps/official/AlekSIS-App-Chronos @@ -1 +1 @@ -Subproject commit 182b37deddc5049fb37c3b0a65ad0516c6147e7b +Subproject commit 455c9d1bb74ae0d727b654a54d8f7c0278308a4e diff --git a/apps/official/AlekSIS-App-DashboardFeeds b/apps/official/AlekSIS-App-DashboardFeeds index ca29920aabf8ee24bc073bae8f75efe7d90c9aea..b89287de8e16e4f87691457a2569e3c16d728935 160000 --- a/apps/official/AlekSIS-App-DashboardFeeds +++ b/apps/official/AlekSIS-App-DashboardFeeds @@ -1 +1 @@ -Subproject commit ca29920aabf8ee24bc073bae8f75efe7d90c9aea +Subproject commit b89287de8e16e4f87691457a2569e3c16d728935 diff --git a/apps/official/AlekSIS-App-Exlibris b/apps/official/AlekSIS-App-Exlibris index 0dbf5bb89e27e55e7347cca2405c7495a803a2dd..f1304db3bb4b8905269eb5f5079f3fe4630c4312 160000 --- a/apps/official/AlekSIS-App-Exlibris +++ b/apps/official/AlekSIS-App-Exlibris @@ -1 +1 @@ -Subproject commit 0dbf5bb89e27e55e7347cca2405c7495a803a2dd +Subproject commit f1304db3bb4b8905269eb5f5079f3fe4630c4312 diff --git a/poetry.lock b/poetry.lock index f450327e896d507b72ca245ee1f5e53be0656188..787b2e8c9c9cffae4371725f98cabb4dcc53b5be 100644 --- a/poetry.lock +++ b/poetry.lock @@ -106,7 +106,7 @@ description = "Python multiprocessing fork with improvements and bugfixes" name = "billiard" optional = true python-versions = "*" -version = "3.6.2.0" +version = "3.6.3.0" [[package]] category = "dev" @@ -427,7 +427,7 @@ description = "Yet another Django audit log app, hopefully the simplest one." name = "django-easy-audit" optional = false python-versions = "*" -version = "1.2.2b1" +version = "1.2.2b2" [package.dependencies] beautifulsoup4 = "*" @@ -541,7 +541,7 @@ description = "Material design for django forms and admin" name = "django-material" optional = false python-versions = "*" -version = "1.6.0" +version = "1.6.3" [package.dependencies] six = "*" @@ -880,7 +880,7 @@ description = "Faker is a Python package that generates fake data for you." name = "faker" optional = false python-versions = ">=3.4" -version = "4.0.0" +version = "4.0.1" [package.dependencies] python-dateutil = ">=2.4" @@ -1029,13 +1029,13 @@ restructuredtext_lint = "*" [[package]] category = "dev" description = "Git Object Database" -name = "gitdb2" +name = "gitdb" optional = false python-versions = ">=3.4" -version = "3.0.0" +version = "4.0.2" [package.dependencies] -smmap2 = ">=2.0.0" +smmap = ">=3.0.1,<4" [[package]] category = "dev" @@ -1043,10 +1043,10 @@ description = "Python Git Library" name = "gitpython" optional = false python-versions = ">=3.4" -version = "3.0.7" +version = "3.1.0" [package.dependencies] -gitdb2 = ">=2.0.0" +gitdb = ">=4.0.1,<5" [[package]] category = "main" @@ -1062,7 +1062,7 @@ description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.8" +version = "2.9" [[package]] category = "dev" @@ -1326,7 +1326,7 @@ description = "Cryptographic library for Python" name = "pycryptodome" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.9.6" +version = "3.9.7" [[package]] category = "dev" @@ -1579,7 +1579,7 @@ description = "Alternative regular expression module, to replace re." name = "regex" optional = false python-versions = "*" -version = "2020.1.8" +version = "2020.2.20" [[package]] category = "main" @@ -1587,16 +1587,16 @@ description = "Python HTTP for Humans." name = "requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.22.0" +version = "2.23.0" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<3.1.0" -idna = ">=2.5,<2.9" +chardet = ">=3.0.2,<4" +idna = ">=2.5,<3" urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" [package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] @@ -1655,10 +1655,10 @@ version = "1.14.0" [[package]] category = "dev" description = "A pure Python implementation of a sliding window memory map manager" -name = "smmap2" +name = "smmap" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.0.5" +version = "3.0.1" [[package]] category = "dev" @@ -1673,8 +1673,8 @@ category = "main" description = "A modern CSS selector implementation for Beautiful Soup." name = "soupsieve" optional = false -python-versions = "*" -version = "1.9.5" +python-versions = ">=3.5" +version = "2.0" [[package]] category = "dev" @@ -1682,7 +1682,7 @@ description = "Python documentation generator" name = "sphinx" optional = false python-versions = ">=3.5" -version = "2.4.1" +version = "2.4.3" [package.dependencies] Jinja2 = ">=2.3" @@ -1754,14 +1754,15 @@ version = "0.5.1" [[package]] category = "dev" -description = "" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" name = "sphinxcontrib-htmlhelp" optional = false -python-versions = "*" -version = "1.0.2" +python-versions = ">=3.5" +version = "1.0.3" [package.extras] -test = ["pytest", "flake8", "mypy", "html5lib"] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest", "html5lib"] [[package]] category = "dev" @@ -1830,12 +1831,12 @@ description = "A collection of helpers and mock objects for unit tests and doc t name = "testfixtures" optional = false python-versions = "*" -version = "6.12.0" +version = "6.14.0" [package.extras] build = ["setuptools-git", "wheel", "twine"] -docs = ["sphinx"] -test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "sybil", "zope.component", "twisted", "mock", "django (<2)", "django"] +docs = ["sphinx", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] +test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] [[package]] category = "dev" @@ -1885,7 +1886,7 @@ description = "Fast, Extensible Progress Meter" name = "tqdm" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.42.1" +version = "4.43.0" [package.extras] dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] @@ -1896,7 +1897,7 @@ description = "Twilio API client and TwiML generator" name = "twilio" optional = false python-versions = "*" -version = "6.35.4" +version = "6.35.5" [package.dependencies] PyJWT = ">=1.4.2" @@ -1971,11 +1972,11 @@ marker = "python_version < \"3.8\"" name = "zipp" optional = false python-versions = ">=3.6" -version = "2.2.0" +version = "3.0.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools"] +testing = ["jaraco.itertools", "func-timeout"] [extras] celery = ["Celery", "django-celery-results", "django-celery-beat", "django-celery-email"] @@ -2024,8 +2025,8 @@ beautifulsoup4 = [ {file = "beautifulsoup4-4.8.2.tar.gz", hash = "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a"}, ] billiard = [ - {file = "billiard-3.6.2.0-py3-none-any.whl", hash = "sha256:26fd494dc3251f8ce1f5559744f18aeed427fdaf29a75d7baae26752a5d3816f"}, - {file = "billiard-3.6.2.0.tar.gz", hash = "sha256:f4e09366653aa3cb3ae8ed16423f9ba1665ff426f087bcdbbed86bf3664fe02c"}, + {file = "billiard-3.6.3.0-py3-none-any.whl", hash = "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede"}, + {file = "billiard-3.6.3.0.tar.gz", hash = "sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a"}, ] black = [ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, @@ -2146,8 +2147,8 @@ django-debug-toolbar = [ {file = "django_debug_toolbar-2.2-py3-none-any.whl", hash = "sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c"}, ] django-easy-audit = [ - {file = "django-easy-audit-1.2.2b1.tar.gz", hash = "sha256:acae5c3ca5cdf9d9e0eaa77d7987e540b7ed4581718ee9c52e67cef27a9c1099"}, - {file = "django_easy_audit-1.2.2b1-py3-none-any.whl", hash = "sha256:c488dc62b8661602944fd795643a507bfa9da8ed08ad271d6ee820b10a08631c"}, + {file = "django-easy-audit-1.2.2b2.tar.gz", hash = "sha256:73b19942999803c0303b35cf303863198897ec84dc10edcb7c967363a64a92dd"}, + {file = "django_easy_audit-1.2.2b2-py3-none-any.whl", hash = "sha256:865af8e2b6f615bcc3ec52a28503aa87d577965d3e1b304d7eb3ed337a319ee9"}, ] django-filter = [ {file = "django-filter-2.2.0.tar.gz", hash = "sha256:c3deb57f0dd7ff94d7dce52a047516822013e2b441bed472b722a317658cfd14"}, @@ -2186,8 +2187,8 @@ django-maintenance-mode = [ {file = "django_maintenance_mode-0.14.0-py2-none-any.whl", hash = "sha256:b4cc24a469ed10897826a28f05d64e6166a58d130e4940ac124ce198cd4cc778"}, ] django-material = [ - {file = "django-material-1.6.0.tar.gz", hash = "sha256:767ab6ad51f906bf773f927e853c2bff6b4ebdd1bd2bf45dbd4ef3e31657c3d5"}, - {file = "django_material-1.6.0-py2.py3-none-any.whl", hash = "sha256:6a30e42f0ceefef1ff325bda0017fa6f6a7fa534b15b8fcc48eb96de4b6adc8e"}, + {file = "django-material-1.6.3.tar.gz", hash = "sha256:f8758afe1beabc16a3c54f5437c7fea15946b7d068eedd89c97d57a363793950"}, + {file = "django_material-1.6.3-py2.py3-none-any.whl", hash = "sha256:502dc88c2f61f190fdc401666e83b47da00cbda98477af6ed8b7d43944ce6407"}, ] django-menu-generator = [ {file = "django-menu-generator-1.0.4.tar.gz", hash = "sha256:ce71a5055c16933c8aff64fb36c21e5cf8b6d505733aceed1252f8b99369a378"}, @@ -2278,8 +2279,8 @@ entrypoints = [ {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] faker = [ - {file = "Faker-4.0.0-py3-none-any.whl", hash = "sha256:047d4d1791bfb3756264da670d99df13d799bb36e7d88774b1585a82d05dbaec"}, - {file = "Faker-4.0.0.tar.gz", hash = "sha256:1b1a58961683b30c574520d0c739c4443e0ef6a185c04382e8cc888273dbebed"}, + {file = "Faker-4.0.1-py3-none-any.whl", hash = "sha256:ee24608768549c2c69e593e9d7a3b53c9498ae735534243ec8390cae5d529f8b"}, + {file = "Faker-4.0.1.tar.gz", hash = "sha256:440d68fe0e46c1658b1975b2497abe0c24a7f772e3892253f31e713ffcc48965"}, ] flake8 = [ {file = "flake8-3.7.9-py2.py3-none-any.whl", hash = "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"}, @@ -2321,21 +2322,21 @@ flake8-polyfill = [ flake8-rst-docstrings = [ {file = "flake8-rst-docstrings-0.0.13.tar.gz", hash = "sha256:b1b619d81d879b874533973ac04ee5d823fdbe8c9f3701bfe802bb41813997b4"}, ] -gitdb2 = [ - {file = "gitdb2-3.0.0-py2.py3-none-any.whl", hash = "sha256:0a84c85d07166cefaf10fa4b728a8a1b847a7f0d081e2fbe083185462158c126"}, - {file = "gitdb2-3.0.0.tar.gz", hash = "sha256:51380b9d8b977b42ac9fc3a8e17af90a56ffc573ca9c6b79f99033b04f29f457"}, +gitdb = [ + {file = "gitdb-4.0.2-py3-none-any.whl", hash = "sha256:284a6a4554f954d6e737cddcff946404393e030b76a282c6640df8efd6b3da5e"}, + {file = "gitdb-4.0.2.tar.gz", hash = "sha256:598e0096bb3175a0aab3a0b5aedaa18a9a25c6707e0eca0695ba1a0baf1b2150"}, ] gitpython = [ - {file = "GitPython-3.0.7-py3-none-any.whl", hash = "sha256:99c77677f31f255e130f3fed4c8e0eebb35f1a09df98ff965fff6774f71688cf"}, - {file = "GitPython-3.0.7.tar.gz", hash = "sha256:99cd0403cecd8a13b95d2e045b9fcaa7837137fcc5ec3105f2c413305d82c143"}, + {file = "GitPython-3.1.0-py3-none-any.whl", hash = "sha256:43da89427bdf18bf07f1164c6d415750693b4d50e28fc9b68de706245147b9dd"}, + {file = "GitPython-3.1.0.tar.gz", hash = "sha256:e426c3b587bd58c482f0b7fe6145ff4ac7ae6c82673fc656f489719abca6f4cb"}, ] html2text = [ {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, ] idna = [ - {file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"}, - {file = "idna-2.8.tar.gz", hash = "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407"}, + {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, + {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, ] imagesize = [ {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, @@ -2535,36 +2536,36 @@ pycodestyle = [ {file = "pycodestyle-2.5.0.tar.gz", hash = "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"}, ] pycryptodome = [ - {file = "pycryptodome-3.9.6-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:5029c46b0d41dfb763c3981c0af68eab029f06fe2b94f2299112fc18cf9e8d6d"}, - {file = "pycryptodome-3.9.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:95d324e603c5cec5d89e8595236bbf59ade5fe3a72d100ce61eebb323d598750"}, - {file = "pycryptodome-3.9.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2a57daef18a2022a5e4b6f7376c9ddd0c2d946e4b1f1e59b837f5bf295be7380"}, - {file = "pycryptodome-3.9.6-cp27-cp27m-win32.whl", hash = "sha256:a719bd708207fa219fcbf4c8ebbcbc52846045f78179d00445b429fdabdbc1c4"}, - {file = "pycryptodome-3.9.6-cp27-cp27m-win_amd64.whl", hash = "sha256:39e5ca2f66d1eac7abcba5ce1a03370d123dc6085620f1cd532dfee27e650178"}, - {file = "pycryptodome-3.9.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f4d2174e168d0eabd1fffaf88b4f62c2b6f30a67b8816f31024b8e48be3e2d75"}, - {file = "pycryptodome-3.9.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ec7d39589f9cfc2a8b83b1d2fc673441757c99d43283e97b2dd46e0e23730db8"}, - {file = "pycryptodome-3.9.6-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:9163fec630495c10c767991e3f8dab32f4427bfb2dfeaa59bb28fe3e52ba66f2"}, - {file = "pycryptodome-3.9.6-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:0a8d5f2dbb4bbe830ace54286b829bfa529f0853bedaab6225fcb2e6d1f7e356"}, - {file = "pycryptodome-3.9.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:5817c0b3c263025d851da96b90cbc7e95348008f88b990e90d10683dba376666"}, - {file = "pycryptodome-3.9.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c41b7e10b72cef00cd63410f31fe50e72dc3a40eafbd146e288384fbe4208064"}, - {file = "pycryptodome-3.9.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f3204006869ab037604b1d9f045c4e84882ddd365e4ee8caa5eb1ff47a59188e"}, - {file = "pycryptodome-3.9.6-cp35-cp35m-win32.whl", hash = "sha256:cdb0ad83a5d6bac986a37fcb7562bcbef0aabae8ea19505bab5cf83c4d18af12"}, - {file = "pycryptodome-3.9.6-cp35-cp35m-win_amd64.whl", hash = "sha256:1259b8ca49662b8a941177357f08147d858595c0042e63ff81e9628e925b5c9d"}, - {file = "pycryptodome-3.9.6-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:09bf05a489fe10f9280a5e0163f195e7b9630cafb15f7d72fb9c8f5eb2afa84f"}, - {file = "pycryptodome-3.9.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fcff8c9d88d58880f7eda2139c7c444552a38f98a9e77ba5970b6e78f54ac358"}, - {file = "pycryptodome-3.9.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9948c2d5c5c0ee45ed44cee0e2eba2ce60a03be006ed3074521f3da3be162e72"}, - {file = "pycryptodome-3.9.6-cp36-cp36m-win32.whl", hash = "sha256:79320f1fc5c9ca682869087c565bb29ca6f334692e940d7365771e9a94382e12"}, - {file = "pycryptodome-3.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:d8e480f65ac7105cbc288eec2417dc61eaac6ed6e75595aa15b8c7c77c53a68b"}, - {file = "pycryptodome-3.9.6-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:07daddb98f98f771ba027f8f835bdb675aeb84effe41ed5221f520b267429354"}, - {file = "pycryptodome-3.9.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:da2d581da279bc7408d38e16ff77754f5448c4352f2acfe530a5d14d8fc6934a"}, - {file = "pycryptodome-3.9.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:012ca77c2105600e3c6aef43188101ac1d95052c633a4ae8fbebffab20c25f8a"}, - {file = "pycryptodome-3.9.6-cp37-cp37m-win32.whl", hash = "sha256:05b4d865710f9a6378d3ada28195ff78e52642d3ecffe6fa9d379d870b9bf29d"}, - {file = "pycryptodome-3.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:9927aa8a8cb4af681279b6f28a1dcb14e0eb556c1aea8413a1e27608a8516e0c"}, - {file = "pycryptodome-3.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de61091dd68326b600422cf731eb4810c4c6363f18a65bccd6061784b7454f5b"}, - {file = "pycryptodome-3.9.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:238d8b6dd27bd1a04816a68aa90a739e6dd23b192fcd83b50f9360958bff192a"}, - {file = "pycryptodome-3.9.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d516df693c195b8da3795e381429bd420e87081b7e6c2871c62c9897c812cda"}, - {file = "pycryptodome-3.9.6-cp38-cp38-win32.whl", hash = "sha256:3e486c5b7228e864665fc479e9f596b2547b5fe29c6f5c8ed3807784d06faed7"}, - {file = "pycryptodome-3.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:887d08beca6368d3d70dc75126607ad76317a9fd07fe61323d8c3cb42add12b6"}, - {file = "pycryptodome-3.9.6.tar.gz", hash = "sha256:bc22ced26ebc46546798fa0141f4418f1db116dec517f0aeaecec87cf7b2416c"}, + {file = "pycryptodome-3.9.7-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:0e10f352ccbbcb5bb2dc4ecaf106564e65702a717d72ab260f9ac4c19753cfc2"}, + {file = "pycryptodome-3.9.7-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:9c739b7795ccf2ef1fdad8d44e539a39ad300ee6786e804ea7f0c6a786eb5343"}, + {file = "pycryptodome-3.9.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9977086e0f93adb326379897437373871b80501e1d176fec63c7f46fb300c862"}, + {file = "pycryptodome-3.9.7-cp27-cp27m-win32.whl", hash = "sha256:83295a3fb5cf50c48631eb5b440cb5e9832d8c14d81d1d45f4497b67a9987de8"}, + {file = "pycryptodome-3.9.7-cp27-cp27m-win_amd64.whl", hash = "sha256:b1e332587b3b195542e77681389c296e1837ca01240399d88803a075447d3557"}, + {file = "pycryptodome-3.9.7-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9378c309aec1f8cd8bad361ed0816a440151b97a2a3f6ffdaba1d1a1fb76873a"}, + {file = "pycryptodome-3.9.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4f94368ce2d65873a87ad867eb3bf63f4ba81eb97a9ee66d38c2b71ce5a7439"}, + {file = "pycryptodome-3.9.7-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:f655addaaaa9974108d4808f4150652589cada96074c87115c52e575bfcd87d5"}, + {file = "pycryptodome-3.9.7-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:9a94fca11fdc161460bd8659c15b6adef45c1b20da86402256eaf3addfaab324"}, + {file = "pycryptodome-3.9.7-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:ea83bcd9d6c03248ebd46e71ac313858e0afd5aa2fa81478c0e653242f3eb476"}, + {file = "pycryptodome-3.9.7-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f"}, + {file = "pycryptodome-3.9.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:426c188c83c10df71f053e04b4003b1437bae5cb37606440e498b00f160d71d0"}, + {file = "pycryptodome-3.9.7-cp35-cp35m-win32.whl", hash = "sha256:d61b012baa8c2b659e9890011358455c0019a4108536b811602d2f638c40802a"}, + {file = "pycryptodome-3.9.7-cp35-cp35m-win_amd64.whl", hash = "sha256:1f4752186298caf2e9ff5354f2e694d607ca7342aa313a62005235d46e28cf04"}, + {file = "pycryptodome-3.9.7-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:767ad0fb5d23efc36a4d5c2fc608ac603f3de028909bcf59abc943e0d0bc5a36"}, + {file = "pycryptodome-3.9.7-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:2fbc472e0b567318fe2052281d5a8c0ae70099b446679815f655e9fbc18c3a65"}, + {file = "pycryptodome-3.9.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9230fcb5d948c3fb40049bace4d33c5d254f8232c2c0bba05d2570aea3ba4520"}, + {file = "pycryptodome-3.9.7-cp36-cp36m-win32.whl", hash = "sha256:8f06556a8f7ea7b1e42eff39726bb0dca1c251205debae64e6eebea3cd7b438a"}, + {file = "pycryptodome-3.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:d6e1bc5c94873bec742afe2dfadce0d20445b18e75c47afc0c115b19e5dd38dd"}, + {file = "pycryptodome-3.9.7-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:3ec3dc2f80f71fd0c955ce48b81bfaf8914c6f63a41a738f28885a1c4892968a"}, + {file = "pycryptodome-3.9.7-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cff31f5a8977534f255f729d5d2467526f2b10563a30bbdade92223e0bf264bd"}, + {file = "pycryptodome-3.9.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ed5761b37615a1f222c5345bbf45272ae2cf8c7dff88a4f53a1e9f977cbb6d95"}, + {file = "pycryptodome-3.9.7-cp37-cp37m-win32.whl", hash = "sha256:f011cd0062e54658b7086a76f8cf0f4222812acc66e219e196ea2d0a8849d0ed"}, + {file = "pycryptodome-3.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:626c0a1d4d83ec6303f970a17158114f75c3ba1736f7f2983f7b40a265861bd8"}, + {file = "pycryptodome-3.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be56bde3312e022d9d1d6afa124556460ad5c844c2fc63642f6af723c098d35"}, + {file = "pycryptodome-3.9.7-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c818dc1f3eace93ee50c2b6b5c2becf7c418fa5dd1ba6fc0ef7db279ea21d5e4"}, + {file = "pycryptodome-3.9.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad"}, + {file = "pycryptodome-3.9.7-cp38-cp38-win32.whl", hash = "sha256:7ac729d9091ed5478af2b4a4f44f5335a98febbc008af619e4569a59fe503e40"}, + {file = "pycryptodome-3.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:c109a26a21f21f695d369ff9b87f5d43e0d6c768d8384e10bc74142bed2e092e"}, + {file = "pycryptodome-3.9.7.tar.gz", hash = "sha256:f1add21b6d179179b3c177c33d18a2186a09cc0d3af41ff5ed3f377360b869f2"}, ] pydocstyle = [ {file = "pydocstyle-5.0.2-py3-none-any.whl", hash = "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586"}, @@ -2654,31 +2655,31 @@ redis = [ {file = "redis-3.4.1.tar.gz", hash = "sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f"}, ] regex = [ - {file = "regex-2020.1.8-cp27-cp27m-win32.whl", hash = "sha256:4e8f02d3d72ca94efc8396f8036c0d3bcc812aefc28ec70f35bb888c74a25161"}, - {file = "regex-2020.1.8-cp27-cp27m-win_amd64.whl", hash = "sha256:e6c02171d62ed6972ca8631f6f34fa3281d51db8b326ee397b9c83093a6b7242"}, - {file = "regex-2020.1.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4eae742636aec40cf7ab98171ab9400393360b97e8f9da67b1867a9ee0889b26"}, - {file = "regex-2020.1.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bd25bb7980917e4e70ccccd7e3b5740614f1c408a642c245019cff9d7d1b6149"}, - {file = "regex-2020.1.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3e77409b678b21a056415da3a56abfd7c3ad03da71f3051bbcdb68cf44d3c34d"}, - {file = "regex-2020.1.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:07b39bf943d3d2fe63d46281d8504f8df0ff3fe4c57e13d1656737950e53e525"}, - {file = "regex-2020.1.8-cp36-cp36m-win32.whl", hash = "sha256:23e2c2c0ff50f44877f64780b815b8fd2e003cda9ce817a7fd00dea5600c84a0"}, - {file = "regex-2020.1.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27429b8d74ba683484a06b260b7bb00f312e7c757792628ea251afdbf1434003"}, - {file = "regex-2020.1.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0e182d2f097ea8549a249040922fa2b92ae28be4be4895933e369a525ba36576"}, - {file = "regex-2020.1.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e3cd21cc2840ca67de0bbe4071f79f031c81418deb544ceda93ad75ca1ee9f7b"}, - {file = "regex-2020.1.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ecc6de77df3ef68fee966bb8cb4e067e84d4d1f397d0ef6fce46913663540d77"}, - {file = "regex-2020.1.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:26ff99c980f53b3191d8931b199b29d6787c059f2e029b2b0c694343b1708c35"}, - {file = "regex-2020.1.8-cp37-cp37m-win32.whl", hash = "sha256:7bcd322935377abcc79bfe5b63c44abd0b29387f267791d566bbb566edfdd146"}, - {file = "regex-2020.1.8-cp37-cp37m-win_amd64.whl", hash = "sha256:10671601ee06cf4dc1bc0b4805309040bb34c9af423c12c379c83d7895622bb5"}, - {file = "regex-2020.1.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:98b8ed7bb2155e2cbb8b76f627b2fd12cf4b22ab6e14873e8641f266e0fb6d8f"}, - {file = "regex-2020.1.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6a6ba91b94427cd49cd27764679024b14a96874e0dc638ae6bdd4b1a3ce97be1"}, - {file = "regex-2020.1.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:6a6ae17bf8f2d82d1e8858a47757ce389b880083c4ff2498dba17c56e6c103b9"}, - {file = "regex-2020.1.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0932941cdfb3afcbc26cc3bcf7c3f3d73d5a9b9c56955d432dbf8bbc147d4c5b"}, - {file = "regex-2020.1.8-cp38-cp38-win32.whl", hash = "sha256:d58e4606da2a41659c84baeb3cfa2e4c87a74cec89a1e7c56bee4b956f9d7461"}, - {file = "regex-2020.1.8-cp38-cp38-win_amd64.whl", hash = "sha256:e7c7661f7276507bce416eaae22040fd91ca471b5b33c13f8ff21137ed6f248c"}, - {file = "regex-2020.1.8.tar.gz", hash = "sha256:d0f424328f9822b0323b3b6f2e4b9c90960b24743d220763c7f07071e0778351"}, + {file = "regex-2020.2.20-cp27-cp27m-win32.whl", hash = "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb"}, + {file = "regex-2020.2.20-cp27-cp27m-win_amd64.whl", hash = "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc"}, + {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"}, + {file = "regex-2020.2.20-cp36-cp36m-win32.whl", hash = "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69"}, + {file = "regex-2020.2.20-cp36-cp36m-win_amd64.whl", hash = "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce"}, + {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab"}, + {file = "regex-2020.2.20-cp37-cp37m-win32.whl", hash = "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431"}, + {file = "regex-2020.2.20-cp37-cp37m-win_amd64.whl", hash = "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux1_i686.whl", hash = "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2"}, + {file = "regex-2020.2.20-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70"}, + {file = "regex-2020.2.20-cp38-cp38-win32.whl", hash = "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d"}, + {file = "regex-2020.2.20-cp38-cp38-win_amd64.whl", hash = "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa"}, + {file = "regex-2020.2.20.tar.gz", hash = "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5"}, ] requests = [ - {file = "requests-2.22.0-py2.py3-none-any.whl", hash = "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"}, - {file = "requests-2.22.0.tar.gz", hash = "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"}, + {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, + {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, ] restructuredtext-lint = [ {file = "restructuredtext_lint-1.3.0.tar.gz", hash = "sha256:97b3da356d5b3a8514d8f1f9098febd8b41463bed6a1d9f126cf0a048b6fd908"}, @@ -2699,21 +2700,21 @@ six = [ {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, ] -smmap2 = [ - {file = "smmap2-2.0.5-py2.py3-none-any.whl", hash = "sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde"}, - {file = "smmap2-2.0.5.tar.gz", hash = "sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a"}, +smmap = [ + {file = "smmap-3.0.1-py2.py3-none-any.whl", hash = "sha256:5fead614cf2de17ee0707a8c6a5f2aa5a2fc6c698c70993ba42f515485ffda78"}, + {file = "smmap-3.0.1.tar.gz", hash = "sha256:171484fe62793e3626c8b05dd752eb2ca01854b0c55a1efc0dc4210fccb65446"}, ] snowballstemmer = [ {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, ] soupsieve = [ - {file = "soupsieve-1.9.5-py2.py3-none-any.whl", hash = "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5"}, - {file = "soupsieve-1.9.5.tar.gz", hash = "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda"}, + {file = "soupsieve-2.0-py2.py3-none-any.whl", hash = "sha256:fcd71e08c0aee99aca1b73f45478549ee7e7fc006d51b37bec9e9def7dc22b69"}, + {file = "soupsieve-2.0.tar.gz", hash = "sha256:e914534802d7ffd233242b785229d5ba0766a7f487385e3f714446a07bf540ae"}, ] sphinx = [ - {file = "Sphinx-2.4.1-py3-none-any.whl", hash = "sha256:5024a67f065fe60d9db2005580074d81f22a02dd8f00a5b1ec3d5f4d42bc88d8"}, - {file = "Sphinx-2.4.1.tar.gz", hash = "sha256:f929b72e0cfe45fa581b8964d54457117863a6a6c9369ecc1a65b8827abd3bf2"}, + {file = "Sphinx-2.4.3-py3-none-any.whl", hash = "sha256:776ff8333181138fae52df65be733127539623bb46cc692e7fa0fcfc80d7aa88"}, + {file = "Sphinx-2.4.3.tar.gz", hash = "sha256:ca762da97c3b5107cbf0ab9e11d3ec7ab8d3c31377266fd613b962ed971df709"}, ] sphinx-autodoc-typehints = [ {file = "sphinx-autodoc-typehints-1.10.3.tar.gz", hash = "sha256:a6b3180167479aca2c4d1ed3b5cb044a70a76cccd6b38662d39288ebd9f0dff0"}, @@ -2732,8 +2733,8 @@ sphinxcontrib-django = [ {file = "sphinxcontrib_django-0.5.1-py2.py3-none-any.whl", hash = "sha256:73ef7fdbf2ed6d4f35b7ae709032bd5ac493d93cedd0624ea7b51bf5fce41267"}, ] sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-1.0.2.tar.gz", hash = "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422"}, - {file = "sphinxcontrib_htmlhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"}, + {file = "sphinxcontrib-htmlhelp-1.0.3.tar.gz", hash = "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"}, + {file = "sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f"}, ] sphinxcontrib-jsmath = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, @@ -2759,8 +2760,8 @@ termcolor = [ {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"}, ] testfixtures = [ - {file = "testfixtures-6.12.0-py2.py3-none-any.whl", hash = "sha256:76eef0c048d6c1ad28bb74ae2b28fa9e3ea3a2f42a56715a4102480b8188e588"}, - {file = "testfixtures-6.12.0.tar.gz", hash = "sha256:c352760016f0e5579a3e5565387e6d582ccad4db9791b6a293fdfc59d4591b97"}, + {file = "testfixtures-6.14.0-py2.py3-none-any.whl", hash = "sha256:799144b3cbef7b072452d9c36cbd024fef415ab42924b96aad49dfd9c763de66"}, + {file = "testfixtures-6.14.0.tar.gz", hash = "sha256:cdfc3d73cb6d3d4dc3c67af84d912e86bf117d30ae25f02fe823382ef99383d2"}, ] "testing.common.database" = [ {file = "testing.common.database-2.0.3-py2.py3-none-any.whl", hash = "sha256:e3ed492bf480a87f271f74c53b262caf5d85c8bc09989a8f534fa2283ec52492"}, @@ -2780,11 +2781,11 @@ toml = [ {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, ] tqdm = [ - {file = "tqdm-4.42.1-py2.py3-none-any.whl", hash = "sha256:fe231261cfcbc6f4a99165455f8f6b9ef4e1032a6e29bccf168b4bf42012f09c"}, - {file = "tqdm-4.42.1.tar.gz", hash = "sha256:251ee8440dbda126b8dfa8a7c028eb3f13704898caaef7caa699b35e119301e2"}, + {file = "tqdm-4.43.0-py2.py3-none-any.whl", hash = "sha256:0d8b5afb66e23d80433102e9bd8b5c8b65d34c2a2255b2de58d97bd2ea8170fd"}, + {file = "tqdm-4.43.0.tar.gz", hash = "sha256:f35fb121bafa030bd94e74fcfd44f3c2830039a2ddef7fc87ef1c2d205237b24"}, ] twilio = [ - {file = "twilio-6.35.4.tar.gz", hash = "sha256:02ece6926e36c1bd9588d95de0ecbcf42d53b14af178866faec974799a26de4b"}, + {file = "twilio-6.35.5.tar.gz", hash = "sha256:d326f4ba5b215b283f127749f38401bdd551746a53f8fb4853b5f47e616370f8"}, ] typed-ast = [ {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, @@ -2831,6 +2832,6 @@ yubiotp = [ {file = "YubiOTP-0.2.2.post1.tar.gz", hash = "sha256:de83b1560226e38b5923f6ab919f962c8c2abb7c722104cb45b2b6db2ac86e40"}, ] zipp = [ - {file = "zipp-2.2.0-py36-none-any.whl", hash = "sha256:d65287feb793213ffe11c0f31b81602be31448f38aeb8ffc2eb286c4f6f6657e"}, - {file = "zipp-2.2.0.tar.gz", hash = "sha256:5c56e330306215cd3553342cfafc73dda2c60792384117893f3a83f8a1209f50"}, + {file = "zipp-3.0.0-py3-none-any.whl", hash = "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2"}, + {file = "zipp-3.0.0.tar.gz", hash = "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a"}, ]