new: basic email template with csv attachments for the orded and each line
This commit is contained in:
parent
5848134067
commit
0148d193ee
8 changed files with 126 additions and 143 deletions
|
|
@ -10,6 +10,7 @@ steps:
|
||||||
- apt-get update -y
|
- apt-get update -y
|
||||||
- apt-get -y install python3.7 python3.7-dev python curl python3.7-distutils
|
- apt-get -y install python3.7 python3.7-dev python curl python3.7-distutils
|
||||||
- curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
|
- curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
|
||||||
|
- cp bernini/settings.py-template bernini/settings.py
|
||||||
- ~/.poetry/bin/poetry install
|
- ~/.poetry/bin/poetry install
|
||||||
- ~/.poetry/bin/poetry run python ./manage.py migrate
|
- ~/.poetry/bin/poetry run python ./manage.py migrate
|
||||||
- ~/.poetry/bin/poetry run python ./manage.py test
|
- ~/.poetry/bin/poetry run python ./manage.py test
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Order {{ order.name }}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<h3>
|
<h3>
|
||||||
Hello {{ order.sent_to.first_name }}!
|
Hello {{ order.sold_to.first_name }}!
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
We are preparing your order {{ order.name }} as it has been confirmed recently - on the {{ order.sent_at }}
|
We are preparing your order {{ order.name }} as it has been confirmed recently - on the {{ order.sold_at }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
Your order detail is as follows:
|
Your order detail is as follows:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -26,11 +23,15 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for line in order.saleorderline_set.all %}
|
{% for line in order.saleorderline_set.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>line.product.name</td>
|
<td>{{ line.product.name }}</td>
|
||||||
<td>line.quantity</td>
|
<td>{{ line.quantity }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Order {{ order.name }}</title>
|
||||||
|
</head>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -9,47 +9,60 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('order', '0001_initial'),
|
("order", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='saleorder',
|
model_name="saleorder",
|
||||||
name='state',
|
name="state",
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='saleorder',
|
model_name="saleorder",
|
||||||
name='sold_at',
|
name="sold_at",
|
||||||
field=models.DateTimeField(default=None, max_length=200, null=True),
|
field=models.DateTimeField(default=None, max_length=200, null=True),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='saleorder',
|
model_name="saleorder",
|
||||||
name='sold_to',
|
name="sold_to",
|
||||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(
|
||||||
|
default=None,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='saleorder',
|
model_name="saleorder",
|
||||||
name='total',
|
name="total",
|
||||||
field=models.FloatField(default=0.0, max_length=200),
|
field=models.FloatField(default=0.0, max_length=200),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='saleorderline',
|
model_name="saleorderline",
|
||||||
name='order',
|
name="order",
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='order.saleorder'),
|
field=models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="order.saleorder",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='saleorderline',
|
model_name="saleorderline",
|
||||||
name='quantity',
|
name="quantity",
|
||||||
field=models.IntegerField(default=0),
|
field=models.IntegerField(default=0),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='saleorder',
|
model_name="saleorder",
|
||||||
name='name',
|
name="name",
|
||||||
field=models.CharField(max_length=200, unique=True),
|
field=models.CharField(max_length=200, unique=True),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='saleorderline',
|
model_name="saleorderline",
|
||||||
name='product',
|
name="product",
|
||||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='order.product'),
|
field=models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||||
|
to="order.product",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ class SaleOrder(BaseModel):
|
||||||
readonly = ["sold_to", "sold_at", "total"]
|
readonly = ["sold_to", "sold_at", "total"]
|
||||||
|
|
||||||
name = models.CharField(max_length=200, unique=True)
|
name = models.CharField(max_length=200, unique=True)
|
||||||
sold_to = models.ForeignKey(User, on_delete=models.DO_NOTHING, default=None, null=True)
|
sold_to = models.ForeignKey(
|
||||||
|
User, on_delete=models.DO_NOTHING, default=None, null=True
|
||||||
|
)
|
||||||
total = models.FloatField(max_length=200, default=0.0)
|
total = models.FloatField(max_length=200, default=0.0)
|
||||||
sold_at = models.DateTimeField(max_length=200, null=True, default=None)
|
sold_at = models.DateTimeField(max_length=200, null=True, default=None)
|
||||||
|
|
||||||
|
|
@ -37,6 +39,14 @@ class SaleOrder(BaseModel):
|
||||||
self.save()
|
self.save()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def as_csv(self) -> str:
|
||||||
|
lines_parsed = ""
|
||||||
|
for line in self.saleorderline_set.all():
|
||||||
|
lines_parsed = f"{lines_parsed}::{line.id}/{line.name}"
|
||||||
|
return f"""name,sold_to,total,sold_at,lines
|
||||||
|
{self.name},{self.sold_to.first_name} {self.sold_to.last_name},{self.total},{self.sold_at},{lines_parsed}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Product(models.Model):
|
class Product(models.Model):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
|
|
@ -48,3 +58,8 @@ class SaleOrderLine(models.Model):
|
||||||
product = models.ForeignKey(Product, on_delete=models.DO_NOTHING, null=True)
|
product = models.ForeignKey(Product, on_delete=models.DO_NOTHING, null=True)
|
||||||
order = models.ForeignKey(SaleOrder, on_delete=models.CASCADE, null=True)
|
order = models.ForeignKey(SaleOrder, on_delete=models.CASCADE, null=True)
|
||||||
quantity = models.IntegerField(default=1)
|
quantity = models.IntegerField(default=1)
|
||||||
|
|
||||||
|
def as_csv(self) -> str:
|
||||||
|
return f"""name,product,quantity
|
||||||
|
{self.name},{self.product.name},{self.quantity}
|
||||||
|
"""
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,18 @@ from order.serializers import SaleOrderSerializer
|
||||||
|
|
||||||
class OrderTests(TestCase):
|
class OrderTests(TestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.product_1: Product = Product.objects.create(name="Product 1", unit_price=10.0)
|
self.product_1: Product = Product.objects.create(
|
||||||
self.product_2: Product = Product.objects.create(name="Product 2", unit_price=20.0)
|
name="Product 1", unit_price=10.0
|
||||||
self.product_3: Product = Product.objects.create(name="Product 3", unit_price=30.0)
|
)
|
||||||
self.product_4: Product = Product.objects.create(name="Product 4", unit_price=40.0)
|
self.product_2: Product = Product.objects.create(
|
||||||
|
name="Product 2", unit_price=20.0
|
||||||
|
)
|
||||||
|
self.product_3: Product = Product.objects.create(
|
||||||
|
name="Product 3", unit_price=30.0
|
||||||
|
)
|
||||||
|
self.product_4: Product = Product.objects.create(
|
||||||
|
name="Product 4", unit_price=40.0
|
||||||
|
)
|
||||||
|
|
||||||
self.order_1: SaleOrder = SaleOrder.objects.create(name="Order 1")
|
self.order_1: SaleOrder = SaleOrder.objects.create(name="Order 1")
|
||||||
self.order_2: SaleOrder = SaleOrder.objects.create(name="Order 2")
|
self.order_2: SaleOrder = SaleOrder.objects.create(name="Order 2")
|
||||||
|
|
@ -21,7 +29,10 @@ class OrderTests(TestCase):
|
||||||
name="Line 1/Order1", order=self.order_1, product=self.product_1
|
name="Line 1/Order1", order=self.order_1, product=self.product_1
|
||||||
)
|
)
|
||||||
self.line_1_2: SaleOrderLine = SaleOrderLine.objects.create(
|
self.line_1_2: SaleOrderLine = SaleOrderLine.objects.create(
|
||||||
name="Line 2/Order 1", quantity=3, order=self.order_1, product=self.product_2
|
name="Line 2/Order 1",
|
||||||
|
quantity=3,
|
||||||
|
order=self.order_1,
|
||||||
|
product=self.product_2,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.line_2_1: SaleOrderLine = SaleOrderLine.objects.create(
|
self.line_2_1: SaleOrderLine = SaleOrderLine.objects.create(
|
||||||
|
|
@ -32,14 +43,10 @@ class OrderTests(TestCase):
|
||||||
name="Line 2/Order2", order=self.order_2, product=self.product_4
|
name="Line 2/Order2", order=self.order_2, product=self.product_4
|
||||||
)
|
)
|
||||||
|
|
||||||
self.user_foo: User = User.objects.create(
|
self.user_foo: User = User.objects.create(username="foo")
|
||||||
username="foo"
|
|
||||||
)
|
|
||||||
self.user_foo.set_password("foo")
|
self.user_foo.set_password("foo")
|
||||||
self.user_foo.save()
|
self.user_foo.save()
|
||||||
self.user_bar = User.objects.create(
|
self.user_bar = User.objects.create(username="bar", password="bar")
|
||||||
username="bar", password="bar"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
self.client.login(username="foo", password="foo")
|
self.client.login(username="foo", password="foo")
|
||||||
|
|
@ -73,35 +80,25 @@ class OrderTests(TestCase):
|
||||||
def test_view_readonly(self):
|
def test_view_readonly(self):
|
||||||
response = self.client.patch(
|
response = self.client.patch(
|
||||||
path="/api/orders/1/",
|
path="/api/orders/1/",
|
||||||
data={
|
data={"sold_at": "foo"},
|
||||||
"sold_at": "foo"
|
|
||||||
},
|
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
response = self.client.patch(
|
response = self.client.patch(
|
||||||
path="/api/orders/1/",
|
path="/api/orders/1/",
|
||||||
data={
|
data={"sold_to": self.user_foo.id},
|
||||||
"sold_to": self.user_foo.id
|
content_type="application/json",
|
||||||
},
|
|
||||||
content_type="application/json"
|
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
def test_view_total(self):
|
def test_view_total(self):
|
||||||
response = self.client.get(
|
response = self.client.get(path="/api/orders/1/total/")
|
||||||
path="/api/orders/1/total/"
|
self.assertEqual(json.loads(response.content.decode())["amount_total"], 70.0)
|
||||||
)
|
response = self.client.get(path="/api/orders/2/total/")
|
||||||
self.assertEqual(json.loads(response.content.decode())['amount_total'], 70.0)
|
self.assertEqual(json.loads(response.content.decode())["amount_total"], 70.0)
|
||||||
response = self.client.get(
|
|
||||||
path="/api/orders/2/total/"
|
|
||||||
)
|
|
||||||
self.assertEqual(json.loads(response.content.decode())['amount_total'], 70.0)
|
|
||||||
|
|
||||||
def test_view_sell(self):
|
def test_view_sell(self):
|
||||||
response = self.client.get(
|
response = self.client.get(path="/api/orders/2/sold/")
|
||||||
path="/api/orders/2/sold/"
|
|
||||||
)
|
|
||||||
self.order_2.refresh_from_db()
|
self.order_2.refresh_from_db()
|
||||||
self.assertEqual(self.order_2.sold_to, self.user_foo)
|
self.assertEqual(self.order_2.sold_to, self.user_foo)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage, EmailMultiAlternatives
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.template.loader import render_to_string
|
||||||
from rest_framework import viewsets, permissions, status
|
from rest_framework import viewsets, permissions, status
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from bernini import settings
|
from bernini import settings
|
||||||
from order.models import SaleOrder, SaleOrderLine, Product
|
from order.models import SaleOrder, SaleOrderLine, Product
|
||||||
from order.serializers import (
|
from order.serializers import (
|
||||||
|
|
@ -33,7 +33,7 @@ class SaleOrderViewSet(viewsets.ModelViewSet):
|
||||||
if request.data.get(field) != SaleOrderSerializer(order).data[field]:
|
if request.data.get(field) != SaleOrderSerializer(order).data[field]:
|
||||||
return Response(
|
return Response(
|
||||||
data=f"You can't manually set the field {field}",
|
data=f"You can't manually set the field {field}",
|
||||||
status=status.HTTP_400_BAD_REQUEST
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
return super(SaleOrderViewSet, self).update(request, *args, **kwargs)
|
return super(SaleOrderViewSet, self).update(request, *args, **kwargs)
|
||||||
|
|
@ -44,7 +44,7 @@ class SaleOrderViewSet(viewsets.ModelViewSet):
|
||||||
if request.data.get(field) != SaleOrderSerializer(order).data[field]:
|
if request.data.get(field) != SaleOrderSerializer(order).data[field]:
|
||||||
return Response(
|
return Response(
|
||||||
data=f"You can't manually set the field {field}",
|
data=f"You can't manually set the field {field}",
|
||||||
status=status.HTTP_400_BAD_REQUEST
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
return super(SaleOrderViewSet, self).partial_update(request, *args, **kwargs)
|
return super(SaleOrderViewSet, self).partial_update(request, *args, **kwargs)
|
||||||
|
|
@ -73,30 +73,46 @@ class SaleOrderViewSet(viewsets.ModelViewSet):
|
||||||
@action(detail=True)
|
@action(detail=True)
|
||||||
def sent(self, request, pk=None):
|
def sent(self, request, pk=None):
|
||||||
order: SaleOrder = self.get_object()
|
order: SaleOrder = self.get_object()
|
||||||
|
|
||||||
if order.sold_at and order.sold_to.email and settings.EMAIL_HOST_USER:
|
if order.sold_at and order.sold_to.email and settings.EMAIL_HOST_USER:
|
||||||
mail = EmailMessage(
|
mail = EmailMultiAlternatives(
|
||||||
f'Order {order.name} from Zapatos Bernini',
|
subject=f"Order {order.name} from Zapatos Bernini",
|
||||||
render(
|
from_email=settings.EMAIL_HOST_USER,
|
||||||
'order_sold.html',
|
to=[order.sold_to.email],
|
||||||
{
|
reply_to=[settings.EMAIL_HOST_USER],
|
||||||
'order': order
|
|
||||||
},
|
|
||||||
),
|
|
||||||
settings.EMAIL_HOST_USER,
|
|
||||||
[order.sold_to.email],
|
|
||||||
reply_to=[settings.EMAIL_HOST_USER]
|
|
||||||
)
|
)
|
||||||
|
mail.attach_alternative(
|
||||||
|
render_to_string(
|
||||||
|
template_name="order_sold.html",
|
||||||
|
context={"order": order},
|
||||||
|
),
|
||||||
|
mimetype="text/html",
|
||||||
|
)
|
||||||
|
mail.attach(
|
||||||
|
filename=f"Bernini order {order.name}.csv",
|
||||||
|
content=order.as_csv(),
|
||||||
|
mimetype="text/csv",
|
||||||
|
)
|
||||||
|
for line in order.saleorderline_set.all():
|
||||||
|
mail.attach(
|
||||||
|
filename=f"Bernini order {order.name}/{line.name}.csv",
|
||||||
|
content=line.as_csv(),
|
||||||
|
mimetype="text/csv",
|
||||||
|
)
|
||||||
mail.send()
|
mail.send()
|
||||||
return Response(status=status.HTTP_200_OK,
|
return Response(
|
||||||
data=f"Email sent! You should receive it at {order.sold_to.email}")
|
status=status.HTTP_200_OK,
|
||||||
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data="Something went wrong on our end")
|
data=f"Email sent! You should receive it at {order.sold_to.email}",
|
||||||
|
)
|
||||||
|
return Response(
|
||||||
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
data="Something went wrong on our end",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderLineViewSet(viewsets.ModelViewSet):
|
class SaleOrderLineViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint that allows orders to be viewed or edited.
|
API endpoint that allows orders to be viewed or edited.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = SaleOrderLine.objects.all().order_by("name")
|
queryset = SaleOrderLine.objects.all().order_by("name")
|
||||||
serializer_class = SaleOrderLineSerializer
|
serializer_class = SaleOrderLineSerializer
|
||||||
|
|
|
||||||
65
poetry.lock
generated
65
poetry.lock
generated
|
|
@ -64,17 +64,6 @@ asgiref = ">=3.2.10,<4"
|
||||||
argon2 = ["argon2-cffi (>=16.1.0)"]
|
argon2 = ["argon2-cffi (>=16.1.0)"]
|
||||||
bcrypt = ["bcrypt"]
|
bcrypt = ["bcrypt"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "django-lint"
|
|
||||||
version = "2.0.4"
|
|
||||||
description = "Static analysis tool for Django projects."
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
pylint = "<1.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "djangorestframework"
|
name = "djangorestframework"
|
||||||
version = "3.12.2"
|
version = "3.12.2"
|
||||||
|
|
@ -86,34 +75,11 @@ python-versions = ">=3.5"
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
django = ">=2.2"
|
django = ">=2.2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "logilab-astng"
|
|
||||||
version = "0.24.3"
|
|
||||||
description = "rebuild a new abstract syntax tree from Python's ast"
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
logilab-common = ">=0.53.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "logilab-common"
|
|
||||||
version = "1.8.1"
|
|
||||||
description = "collection of low-level Python packages and modules used by Logilab projects"
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
mypy-extensions = "*"
|
|
||||||
typing-extensions = "*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy-extensions"
|
name = "mypy-extensions"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||||
category = "main"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
|
@ -133,18 +99,6 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pylint"
|
|
||||||
version = "0.28.0"
|
|
||||||
description = "python code static checker"
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
logilab-astng = ">=0.24.3"
|
|
||||||
logilab-common = ">=0.53.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2020.5"
|
version = "2020.5"
|
||||||
|
|
@ -197,7 +151,7 @@ python-versions = "*"
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "3.7.4.3"
|
version = "3.7.4.3"
|
||||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||||
category = "main"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
|
@ -212,7 +166,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
content-hash = "aaf6dbea36a6c1cea3e3e54eaea7e189f818a37eabcb0362b93307b790d2a634"
|
content-hash = "77ab632e41ccac8c1f660cfa95a2286225dc42b526eae2f5500f3d2d000a5f92"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
appdirs = [
|
appdirs = [
|
||||||
|
|
@ -234,20 +188,10 @@ django = [
|
||||||
{file = "Django-3.1.5-py3-none-any.whl", hash = "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"},
|
{file = "Django-3.1.5-py3-none-any.whl", hash = "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"},
|
||||||
{file = "Django-3.1.5.tar.gz", hash = "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7"},
|
{file = "Django-3.1.5.tar.gz", hash = "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7"},
|
||||||
]
|
]
|
||||||
django-lint = [
|
|
||||||
{file = "django-lint-2.0.4.tar.gz", hash = "sha256:25f28091d40f4586aa46cd3cab3d98665067ade95d7a45013ab24f03bb9989f1"},
|
|
||||||
]
|
|
||||||
djangorestframework = [
|
djangorestframework = [
|
||||||
{file = "djangorestframework-3.12.2-py3-none-any.whl", hash = "sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7"},
|
{file = "djangorestframework-3.12.2-py3-none-any.whl", hash = "sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7"},
|
||||||
{file = "djangorestframework-3.12.2.tar.gz", hash = "sha256:0898182b4737a7b584a2c73735d89816343369f259fea932d90dc78e35d8ac33"},
|
{file = "djangorestframework-3.12.2.tar.gz", hash = "sha256:0898182b4737a7b584a2c73735d89816343369f259fea932d90dc78e35d8ac33"},
|
||||||
]
|
]
|
||||||
logilab-astng = [
|
|
||||||
{file = "logilab-astng-0.24.3.tar.gz", hash = "sha256:e08fba39689e5a4dfa175749874811e516f019656544874defe05154073f69d4"},
|
|
||||||
]
|
|
||||||
logilab-common = [
|
|
||||||
{file = "logilab-common-1.8.1.tar.gz", hash = "sha256:4a50659f6f952af58654f89f65136214025ca203406f3508642d5c2e1c83d30c"},
|
|
||||||
{file = "logilab_common-1.8.1-py3-none-any.whl", hash = "sha256:3f150dd8d8a67d8965032c6fcfb2a31adeaf587e0fe15416c5f6598e03cae844"},
|
|
||||||
]
|
|
||||||
mypy-extensions = [
|
mypy-extensions = [
|
||||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
{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 = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||||
|
|
@ -260,9 +204,6 @@ pygments = [
|
||||||
{file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
|
{file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
|
||||||
{file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"},
|
{file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"},
|
||||||
]
|
]
|
||||||
pylint = [
|
|
||||||
{file = "pylint-0.28.0.tar.gz", hash = "sha256:310a03c37148e53521cb4ab704bdba0a2a59af091261c89dde3239320f8fa458"},
|
|
||||||
]
|
|
||||||
pytz = [
|
pytz = [
|
||||||
{file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"},
|
{file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"},
|
||||||
{file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"},
|
{file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"},
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ Pygments = "^2.7.4"
|
||||||
PyYAML = "^5.4.1"
|
PyYAML = "^5.4.1"
|
||||||
uritemplate = "^3.0.1"
|
uritemplate = "^3.0.1"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = "^20.8b1"
|
black = "^20.8b1"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue