diff --git a/api/api_errors.py b/api/api_errors.py index 42429b1..8c83187 100644 --- a/api/api_errors.py +++ b/api/api_errors.py @@ -44,26 +44,28 @@ API_ERROR_VALIDATION = { def make_error_object(ex: Exception): + data = { + "status": "error" + } try: if type(ex.args[0]) != tuple: - raise Exception(API_ERROR_INTERNAL_ERROR) + raise ex - data = { - "error": { - "code": ex.args[0][0], - "message": ex.args[0][1] - } + data["error"] = { + "code": ex.args[0][0], + "message": ex.args[0][1] } if len(ex.args) >= 2: data["error"]["related"] = ex.args[1] return data - except Exception: + + except BaseException as err: traceback.print_exc() - return { - "error": { - "code": API_ERROR_INTERNAL_ERROR[0], - "message": API_ERROR_INTERNAL_ERROR[1] - } + data["error"] = { + "code": API_ERROR_INTERNAL_ERROR[0], + "message": API_ERROR_INTERNAL_ERROR[1], + "related": f"Exception {type(err)}: {str(err)}" } + return data diff --git a/api/api_methods.py b/api/api_methods.py index 817c188..3b0b125 100644 --- a/api/api_methods.py +++ b/api/api_methods.py @@ -102,6 +102,7 @@ def account_verify_phone(params): def account_get(params): user = _reqire_access_token(params) return api_make_response({ + "id": user.id, "name": user.name, "surname": user.surname, "email": user.email, @@ -152,7 +153,7 @@ api_methods = { "params": [ ], - "returns": "Поля пользователя (name, surname, email, phone, phone_verified)." + "returns": "Поля пользователя (id, name, surname, email, phone, phone_verified)." }, "account.verifyPhone": { diff --git a/api/api_utils.py b/api/api_utils.py index 6a5c0d9..9762456 100644 --- a/api/api_utils.py +++ b/api/api_utils.py @@ -7,7 +7,7 @@ def __make_invalid_argument_type_error(name, value, except_type): def api_make_response(response): - return {"response": API_OK_OBJ | response} + return API_OK_OBJ | {"response": response} def api_get_param_int(params: dict, name: str, required=True, default=0): diff --git a/order/admin.py b/order/admin.py index de3fb3c..791e6d2 100644 --- a/order/admin.py +++ b/order/admin.py @@ -10,4 +10,5 @@ class CityAdmin(admin.ModelAdmin): @admin.register(Order) class OrderAdmin(admin.ModelAdmin): - pass + list_display = ['owner', 'phone', 'name', 'create_time', 'moderated', 'published'] + readonly_fields = ['create_time'] diff --git a/order/forms.py b/order/forms.py index db99637..e9c0261 100644 --- a/order/forms.py +++ b/order/forms.py @@ -31,3 +31,7 @@ class UnregisteredUserOrderCreationForm(BaseOrderCreationForm): RegexValidator(regex="^\\+7[0-9]{10}$"), ]) email = forms.EmailField(required=True) + + class Meta: + model = Order + fields = BaseOrderCreationForm.Meta.fields + ['phone', 'email'] diff --git a/order/migrations/0009_order_date_end_order_date_start.py b/order/migrations/0009_order_date_end_order_date_start.py new file mode 100644 index 0000000..23a6d2c --- /dev/null +++ b/order/migrations/0009_order_date_end_order_date_start.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.1 on 2022-09-27 22:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0008_alter_order_approximate_price_alter_order_square_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='date_end', + field=models.DateField(blank=True, default=None), + ), + migrations.AddField( + model_name='order', + name='date_start', + field=models.DateField(blank=True, default=None), + ), + ] diff --git a/order/migrations/0010_alter_order_date_end_alter_order_date_start.py b/order/migrations/0010_alter_order_date_end_alter_order_date_start.py new file mode 100644 index 0000000..5ca8d7a --- /dev/null +++ b/order/migrations/0010_alter_order_date_end_alter_order_date_start.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.1 on 2022-09-27 22:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0009_order_date_end_order_date_start'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='date_end', + field=models.DateField(blank=True, default=None, verbose_name='Дата окончания'), + ), + migrations.AlterField( + model_name='order', + name='date_start', + field=models.DateField(blank=True, default=None, verbose_name='Дата начала'), + ), + ] diff --git a/order/migrations/0011_order_email_order_phone.py b/order/migrations/0011_order_email_order_phone.py new file mode 100644 index 0000000..47cdd65 --- /dev/null +++ b/order/migrations/0011_order_email_order_phone.py @@ -0,0 +1,25 @@ +# Generated by Django 4.1.1 on 2022-09-28 08:04 + +import django.core.validators +from django.db import migrations, models +import order.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0010_alter_order_date_end_alter_order_date_start'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='email', + field=models.EmailField(max_length=254, null=True, validators=[order.models._email_validate], verbose_name='Email'), + ), + migrations.AddField( + model_name='order', + name='phone', + field=models.CharField(max_length=16, null=True, validators=[django.core.validators.RegexValidator(regex='^\\+7[0-9]{10}$'), order.models._phone_validate], verbose_name='Телефон'), + ), + ] diff --git a/order/migrations/0012_alter_order_date_end_alter_order_date_start.py b/order/migrations/0012_alter_order_date_end_alter_order_date_start.py new file mode 100644 index 0000000..3b5a582 --- /dev/null +++ b/order/migrations/0012_alter_order_date_end_alter_order_date_start.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.1 on 2022-09-28 08:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0011_order_email_order_phone'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='date_end', + field=models.DateField(default=None, null=True, verbose_name='Дата окончания'), + ), + migrations.AlterField( + model_name='order', + name='date_start', + field=models.DateField(default=None, null=True, verbose_name='Дата начала'), + ), + ] diff --git a/order/migrations/0013_alter_order_date_end_alter_order_date_start.py b/order/migrations/0013_alter_order_date_end_alter_order_date_start.py new file mode 100644 index 0000000..12b872c --- /dev/null +++ b/order/migrations/0013_alter_order_date_end_alter_order_date_start.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.1 on 2022-09-28 08:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0012_alter_order_date_end_alter_order_date_start'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='date_end', + field=models.DateField(blank=True, default=None, null=True, verbose_name='Дата окончания'), + ), + migrations.AlterField( + model_name='order', + name='date_start', + field=models.DateField(blank=True, default=None, null=True, verbose_name='Дата начала'), + ), + ] diff --git a/order/migrations/0014_order_create_time.py b/order/migrations/0014_order_create_time.py new file mode 100644 index 0000000..9188ff7 --- /dev/null +++ b/order/migrations/0014_order_create_time.py @@ -0,0 +1,19 @@ +# Generated by Django 4.1.1 on 2022-09-28 08:47 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0013_alter_order_date_end_alter_order_date_start'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='create_time', + field=models.DateTimeField(default=datetime.datetime.now, editable=False), + ), + ] diff --git a/order/migrations/0015_order_moderated_order_published_and_more.py b/order/migrations/0015_order_moderated_order_published_and_more.py new file mode 100644 index 0000000..14478d9 --- /dev/null +++ b/order/migrations/0015_order_moderated_order_published_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.1.1 on 2022-09-28 08:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0014_order_create_time'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='moderated', + field=models.BooleanField(default=False, verbose_name='Модерирован'), + ), + migrations.AddField( + model_name='order', + name='published', + field=models.BooleanField(default=False, verbose_name='Опубликован'), + ), + migrations.AlterField( + model_name='order', + name='create_time', + field=models.DateTimeField(default=datetime.datetime.now, editable=False, verbose_name='Время создания'), + ), + ] diff --git a/order/migrations/0016_alter_order_email_alter_order_moderated_and_more.py b/order/migrations/0016_alter_order_email_alter_order_moderated_and_more.py new file mode 100644 index 0000000..3e84703 --- /dev/null +++ b/order/migrations/0016_alter_order_email_alter_order_moderated_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.1.1 on 2022-09-28 09:55 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import order.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('order', '0015_order_moderated_order_published_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='email', + field=models.EmailField(blank=True, max_length=254, null=True, validators=[order.models._email_validate], verbose_name='Email'), + ), + migrations.AlterField( + model_name='order', + name='moderated', + field=models.BooleanField(default=True, verbose_name='Модерирован'), + ), + migrations.AlterField( + model_name='order', + name='owner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='owner', to=settings.AUTH_USER_MODEL, verbose_name='Владелец'), + ), + migrations.AlterField( + model_name='order', + name='phone', + field=models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(regex='^\\+7[0-9]{10}$'), order.models._phone_validate], verbose_name='Телефон'), + ), + ] diff --git a/order/models.py b/order/models.py index 8af41d0..ae7965a 100644 --- a/order/models.py +++ b/order/models.py @@ -1,7 +1,10 @@ +from django.core.exceptions import * from django.core.validators import RegexValidator from django.db import models +from django.db.models import Q from account.models import SiteUser +from datetime import datetime class City(models.Model): @@ -18,6 +21,50 @@ class City(models.Model): return City.objects.order_by('name').values_list('code', 'name') +def _phone_validate(phone, order=None): + if phone is not None: + err_obj = {"phone": f'Телефон {phone} уже используется другим аккаунтом'} + if SiteUser.objects.filter(phone=phone).count() > 0: + raise ValidationError( + err_obj, + code='permission' + ) + if order is None: + if Order.objects.filter(phone=phone).count() > 0: + raise ValidationError( + err_obj, + code='permission' + ) + else: + if Order.objects.filter(~Q(id=order.id), phone=phone).count() > 0: + raise ValidationError( + err_obj, + code='permission' + ) + + +def _email_validate(email, order=None): + if email is not None: + err_obj = {'email': f'Почта {email} уже используется другим аккаунтом'} + if SiteUser.objects.filter(email=email).count(): + raise ValidationError( + err_obj, + code='permission' + ) + if order is None: + if Order.objects.filter(email=email).count() > 0: + raise ValidationError( + err_obj, + code='permission' + ) + else: + if Order.objects.filter(~Q(id=order.id), email=email).count() > 0: + raise ValidationError( + err_obj, + code='permission' + ) + + class Order(models.Model): # основные поля: название и описание name = models.CharField(max_length=200, verbose_name="Название заказа") @@ -112,13 +159,76 @@ class Order(models.Model): # примерная цена approximate_price = models.DecimalField(max_digits=12, decimal_places=2, blank=False, verbose_name="Цена") - date_start = models.DateField(blank=True, default=None, verbose_name="Дата начала") - date_end = models.DateField(blank=True, default=None, verbose_name="Дата окончания") + date_start = models.DateField(null=True, blank=True, default=None, verbose_name="Дата начала") + date_end = models.DateField(null=True, blank=True, default=None, verbose_name="Дата окончания") address_city = models.ForeignKey(City, on_delete=models.CASCADE, blank=False, related_name="address_city", verbose_name="Город") address_text = models.CharField(max_length=70, blank=True, verbose_name="Улица, дом") - owner = models.ForeignKey(SiteUser, on_delete=models.CASCADE, null=True, related_name="owner", + owner = models.ForeignKey(SiteUser, on_delete=models.CASCADE, blank=True, null=True, related_name="owner", verbose_name="Владелец") + email = models.EmailField(null=True, blank=True, verbose_name="Email") + phone = models.CharField(null=True, blank=True, max_length=16, verbose_name="Телефон", validators=[ + RegexValidator(regex="^\\+7[0-9]{10}$") + ]) + + create_time = models.DateTimeField(default=datetime.now, editable=False, verbose_name="Время создания") + + moderated = models.BooleanField(default=True, verbose_name="Модерирован") + published = models.BooleanField(default=False, verbose_name="Опубликован") + + def clean(self): + errors = {} + try: + if self.pk is None: + _phone_validate(self.phone) + else: + _phone_validate(self.phone, self) + except ValidationError as e: + errors["phone"] = [e] + + try: + if self.pk is None: + _email_validate(self.email) + else: + _email_validate(self.email, self) + except ValidationError as e: + errors["email"] = [e] + + if len(errors) > 0: + raise ValidationError(errors) + + def save(self, *args, **kwargs): + if self.pk is None: + if self.owner is None: + if self.phone is None or self.email is None: + raise Exception("Could not save: need contact information") + + if self.owner is not None: + self.phone = None + self.email = None + + self.full_clean() + + super().save(*args, **kwargs) + + def __str__(self): + return self.name + + @staticmethod + def get_all_for_user(user): + if user.is_staff: + return Order.objects.filter().order_by('create_time') + else: + return Order.objects.filter(published=True, moderated=True).order_by('create_time') + + @staticmethod + def get_for_user_by_id(user, order_id): + q = Order.get_all_for_user(user).filter(id=order_id) + if len(q) == 0: + return None + else: + return q[0] + diff --git a/order/urls.py b/order/urls.py index 1e0cf97..0732570 100644 --- a/order/urls.py +++ b/order/urls.py @@ -20,5 +20,6 @@ from . import views urlpatterns = [ path('', views.orders_list, name='orders-list'), path('create', views.order_create, name='order-create'), + path('view/', views.order_view, name='order-view'), ] diff --git a/order/views.py b/order/views.py index c5ba10c..96db3f4 100644 --- a/order/views.py +++ b/order/views.py @@ -1,26 +1,42 @@ from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import * +from django.contrib.auth.decorators import login_required def orders_list(request): - return render(request, 'orders/orders-list.html') + if request.user.is_authenticated: + orders = Order.get_all_for_user(request.user) + return render(request, 'orders/orders-list.html', {'orders': orders[:50]}) + else: + return HttpResponseRedirect('/accounts/register') def order_create(request): if request.user.is_authenticated: if request.method == 'POST': - form = BaseOrderCreationForm(request.POST) + order = Order(owner=request.user) + form = BaseOrderCreationForm(request.POST, instance=order) if form.is_valid(): - return HttpResponseRedirect('/account') + form.save() + return HttpResponseRedirect('/orders/') else: form = BaseOrderCreationForm() else: if request.method == 'POST': - form = UnregisteredUserOrderCreationForm(request.POST) + order = Order() + form = UnregisteredUserOrderCreationForm(request.POST, instance=order) if form.is_valid(): - return HttpResponseRedirect('/account') + form.save() + return HttpResponseRedirect('/dev') else: form = UnregisteredUserOrderCreationForm() print(form.visible_fields) return render(request, 'orders/order-create.html', {'form': form}) + + +@login_required +def order_view(request, order_id): + order = Order.get_all_for_user(request.user) + order = order.select_related('address_city').get(id=order_id) + return render(request, 'orders/order-view.html', {"order": order}) diff --git a/static/css/style.css b/static/css/style.css index f3d6ae7..6a3c306 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -129,6 +129,10 @@ header > * { display: block; } + #profile-menu-dropdown > .dropdown-content{ + right: 1.5em; + } + header > a { display: none; } diff --git a/static/images/test4.jpg b/static/images/test4.jpg new file mode 100644 index 0000000..11e4d17 Binary files /dev/null and b/static/images/test4.jpg differ diff --git a/static/images/test5.jpg b/static/images/test5.jpg new file mode 100644 index 0000000..4d2fe4c Binary files /dev/null and b/static/images/test5.jpg differ diff --git a/templates/base.html b/templates/base.html index a90f4a4..98f124d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -44,7 +44,7 @@ -