From 85e55282a05e431c36539060837b0e72c9c5d3c8 Mon Sep 17 00:00:00 2001 From: Kseninia Mikhaylova Date: Fri, 17 May 2024 16:51:54 +0300 Subject: [PATCH] add save data in db --- .vscode/PythonImportHelper-v2-Completion.json | 248 +++++++++++++++--- back/api/admin.py | 3 +- back/api/models.py | 5 + back/api/serializers.py | 10 +- back/api/tracer.py | 100 ++++--- back/api/views.py | 64 ++++- back/back/settings.py | 5 + back/back/urls.py | 4 +- front/components.d.ts | 2 + front/src/components/Floorplan.vue | 17 ++ front/src/components/Home.vue | 3 + front/src/main.ts | 2 + front/src/stores/floorplan.ts | 33 +++ pyproject.toml | 9 - 14 files changed, 404 insertions(+), 101 deletions(-) create mode 100644 front/src/components/Floorplan.vue create mode 100644 front/src/stores/floorplan.ts diff --git a/.vscode/PythonImportHelper-v2-Completion.json b/.vscode/PythonImportHelper-v2-Completion.json index c4a002a..31ff75e 100644 --- a/.vscode/PythonImportHelper-v2-Completion.json +++ b/.vscode/PythonImportHelper-v2-Completion.json @@ -392,6 +392,15 @@ "detail": "unittest", "documentation": {} }, + { + "label": "io", + "kind": 6, + "isExtraImport": true, + "importPath": "io", + "description": "io", + "detail": "io", + "documentation": {} + }, { "label": "StringIO", "importPath": "io", @@ -1612,6 +1621,54 @@ "detail": "django.db", "documentation": {} }, + { + "label": "migrations", + "importPath": "django.db", + "description": "django.db", + "isExtraImport": true, + "detail": "django.db", + "documentation": {} + }, + { + "label": "models", + "importPath": "django.db", + "description": "django.db", + "isExtraImport": true, + "detail": "django.db", + "documentation": {} + }, + { + "label": "migrations", + "importPath": "django.db", + "description": "django.db", + "isExtraImport": true, + "detail": "django.db", + "documentation": {} + }, + { + "label": "models", + "importPath": "django.db", + "description": "django.db", + "isExtraImport": true, + "detail": "django.db", + "documentation": {} + }, + { + "label": "migrations", + "importPath": "django.db", + "description": "django.db", + "isExtraImport": true, + "detail": "django.db", + "documentation": {} + }, + { + "label": "models", + "importPath": "django.db", + "description": "django.db", + "isExtraImport": true, + "detail": "django.db", + "documentation": {} + }, { "label": "models", "importPath": "django.db", @@ -1677,35 +1734,30 @@ "documentation": {} }, { - "label": "Image", - "importPath": "PIL", - "description": "PIL", + "label": "base64", + "kind": 6, "isExtraImport": true, - "detail": "PIL", + "importPath": "base64", + "description": "base64", + "detail": "base64", "documentation": {} }, { - "label": "Bitmap", - "importPath": "potrace", - "description": "potrace", + "label": "cv2", + "kind": 6, "isExtraImport": true, - "detail": "potrace", + "importPath": "cv2", + "description": "cv2", + "detail": "cv2", "documentation": {} }, { - "label": "POTRACE_TURNPOLICY_MINORITY", - "importPath": "potrace", - "description": "potrace", + "label": "numpy", + "kind": 6, "isExtraImport": true, - "detail": "potrace", - "documentation": {} - }, - { - "label": "render", - "importPath": "django.shortcuts", - "description": "django.shortcuts", - "isExtraImport": true, - "detail": "django.shortcuts", + "importPath": "numpy", + "description": "numpy", + "detail": "numpy", "documentation": {} }, { @@ -1717,19 +1769,19 @@ "documentation": {} }, { - "label": "csrf_exempt", - "importPath": "django.views.decorators.csrf", - "description": "django.views.decorators.csrf", + "label": "MultiPartParser", + "importPath": "rest_framework.parsers", + "description": "rest_framework.parsers", "isExtraImport": true, - "detail": "django.views.decorators.csrf", + "detail": "rest_framework.parsers", "documentation": {} }, { - "label": "HttpResponse", - "importPath": "django.http", - "description": "django.http", + "label": "APIView", + "importPath": "rest_framework.views", + "description": "rest_framework.views", "isExtraImport": true, - "detail": "django.http", + "detail": "rest_framework.views", "documentation": {} }, { @@ -1740,6 +1792,30 @@ "detail": "django.http", "documentation": {} }, + { + "label": "parse_image", + "importPath": "api.tracer", + "description": "api.tracer", + "isExtraImport": true, + "detail": "api.tracer", + "documentation": {} + }, + { + "label": "read_image", + "importPath": "api.tracer", + "description": "api.tracer", + "isExtraImport": true, + "detail": "api.tracer", + "documentation": {} + }, + { + "label": "numpy_zip_str_to_arr", + "importPath": "api.tracer", + "description": "api.tracer", + "isExtraImport": true, + "detail": "api.tracer", + "documentation": {} + }, { "label": "get_asgi_application", "importPath": "django.core.asgi", @@ -1788,6 +1864,14 @@ "detail": "api", "documentation": {} }, + { + "label": "csrf_exempt", + "importPath": "django.views.decorators.csrf", + "description": "django.views.decorators.csrf", + "isExtraImport": true, + "detail": "django.views.decorators.csrf", + "documentation": {} + }, { "label": "get_wsgi_application", "importPath": "django.core.wsgi", @@ -8344,10 +8428,37 @@ "kind": 6, "importPath": "back.api.migrations.0007_floorplan_alter_product_image1_alter_product_image2_and_more", "description": "back.api.migrations.0007_floorplan_alter_product_image1_alter_product_image2_and_more", - "peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0006_product_image2_product_image3'),\n ]\n operations = [\n migrations.CreateModel(\n name='FloorPlan',\n fields=[\n ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n ('title', models.CharField(max_length=200)),", + "peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0006_product_image2_product_image3'),\n ]\n operations = [\n migrations.CreateModel(\n name='Floorplan',\n fields=[\n ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n ('title', models.CharField(max_length=200)),", "detail": "back.api.migrations.0007_floorplan_alter_product_image1_alter_product_image2_and_more", "documentation": {} }, + { + "label": "Migration", + "kind": 6, + "importPath": "back.api.migrations.0008_alter_floorplan_np_field", + "description": "back.api.migrations.0008_alter_floorplan_np_field", + "peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0007_floorplan_alter_product_image1_alter_product_image2_and_more'),\n ]\n operations = [\n migrations.AlterField(\n model_name='floorplan',\n name='np_field',\n field=models.TextField(),\n ),", + "detail": "back.api.migrations.0008_alter_floorplan_np_field", + "documentation": {} + }, + { + "label": "Migration", + "kind": 6, + "importPath": "back.api.migrations.0009_alter_floorplan_np_field", + "description": "back.api.migrations.0009_alter_floorplan_np_field", + "peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0008_alter_floorplan_np_field'),\n ]\n operations = [\n migrations.AlterField(\n model_name='floorplan',\n name='np_field',\n field=models.BinaryField(),\n ),", + "detail": "back.api.migrations.0009_alter_floorplan_np_field", + "documentation": {} + }, + { + "label": "Migration", + "kind": 6, + "importPath": "back.api.migrations.0010_alter_floorplan_np_field", + "description": "back.api.migrations.0010_alter_floorplan_np_field", + "peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('api', '0009_alter_floorplan_np_field'),\n ]\n operations = [\n migrations.AlterField(\n model_name='floorplan',\n name='np_field',\n field=models.TextField(),\n ),", + "detail": "back.api.migrations.0010_alter_floorplan_np_field", + "documentation": {} + }, { "label": "ApiConfig", "kind": 6, @@ -8366,6 +8477,15 @@ "detail": "back.api.models", "documentation": {} }, + { + "label": "Floorplan", + "kind": 6, + "importPath": "back.api.models", + "description": "back.api.models", + "peekOfCode": "class Floorplan(models.Model):\n title = models.CharField(max_length=200)\n np_field = models.TextField()", + "detail": "back.api.models", + "documentation": {} + }, { "label": "ProductSerializer", "kind": 6, @@ -8376,20 +8496,74 @@ "documentation": {} }, { - "label": "file_to_svg", + "label": "FloorplanSerializer", + "kind": 6, + "importPath": "back.api.serializers", + "description": "back.api.serializers", + "peekOfCode": "class FloorplanSerializer(serializers.Serializer):\n title = serializers.CharField(max_length=200)\n np_field = serializers.CharField()\n def create(self,validated_data):\n return Floorplan.objects.create(**validated_data)", + "detail": "back.api.serializers", + "documentation": {} + }, + { + "label": "numpy_arr_to_zip_str", "kind": 2, "importPath": "back.api.tracer", "description": "back.api.tracer", - "peekOfCode": "def file_to_svg(filename: str):\n try:\n image = Image.open(filename)\n except IOError:\n print(\"Image (%s) could not be loaded.\" % filename)\n return\n bm = Bitmap(image, blacklevel=0.5)\n # bm.invert()\n plist = bm.trace(\n turdsize=2,", + "peekOfCode": "def numpy_arr_to_zip_str(arr):\n f = io.BytesIO()\n np.savez_compressed(f, arr=arr)\n return base64.b64encode(f.getvalue())\ndef numpy_zip_str_to_arr(zip_str):\n f = io.BytesIO(base64.b64decode(zip_str))\n return np.load(f)['arr'].tolist()\ndef read_image(content: bytes) -> np.ndarray:\n \"\"\"\n Image bytes to OpenCV image", "detail": "back.api.tracer", "documentation": {} }, { - "label": "products", + "label": "numpy_zip_str_to_arr", "kind": 2, + "importPath": "back.api.tracer", + "description": "back.api.tracer", + "peekOfCode": "def numpy_zip_str_to_arr(zip_str):\n f = io.BytesIO(base64.b64decode(zip_str))\n return np.load(f)['arr'].tolist()\ndef read_image(content: bytes) -> np.ndarray:\n \"\"\"\n Image bytes to OpenCV image\n :param content: Image bytes\n :returns OpenCV image\n :raises TypeError: If content is not bytes\n :raises ValueError: If content does not represent an image", + "detail": "back.api.tracer", + "documentation": {} + }, + { + "label": "read_image", + "kind": 2, + "importPath": "back.api.tracer", + "description": "back.api.tracer", + "peekOfCode": "def read_image(content: bytes) -> np.ndarray:\n \"\"\"\n Image bytes to OpenCV image\n :param content: Image bytes\n :returns OpenCV image\n :raises TypeError: If content is not bytes\n :raises ValueError: If content does not represent an image\n \"\"\"\n if not isinstance(content, bytes):\n raise TypeError(f\"Expected 'content' to be bytes, received: {type(content)}\")", + "detail": "back.api.tracer", + "documentation": {} + }, + { + "label": "parse_image", + "kind": 2, + "importPath": "back.api.tracer", + "description": "back.api.tracer", + "peekOfCode": "def parse_image(img):\n (img_h, img_w) = img.shape[:2]\n t = 1200\n w = t\n h = int((img_h / img_w) * t)\n img = cv2.resize(img, (w, h))\n gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n gray = 255 - gray\n gray = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]\n gray = cv2.blur(gray, (10, 5))", + "detail": "back.api.tracer", + "documentation": {} + }, + { + "label": "Products", + "kind": 6, "importPath": "back.api.views", "description": "back.api.views", - "peekOfCode": "def products(request):\n \"\"\"\n List all task snippets\n \"\"\"\n if request.method == \"GET\":\n tasks = Product.objects.all()\n serializer = ProductSerializer(tasks, many=True)\n return JsonResponse(serializer.data, safe=False)\n elif request.method == \"POST\":\n data = JSONParser().parse(request)", + "peekOfCode": "class Products(APIView):\n def get(self, request):\n tasks = Product.objects.all()\n serializer = ProductSerializer(tasks, many=True)\n return JsonResponse(serializer.data, safe=False)\n def post(self, request):\n data = JSONParser().parse(request)\n serializer = ProductSerializer(data=data)\n if serializer.is_valid():\n serializer.save()", + "detail": "back.api.views", + "documentation": {} + }, + { + "label": "FloorplanView", + "kind": 6, + "importPath": "back.api.views", + "description": "back.api.views", + "peekOfCode": "class FloorplanView(APIView):\n parser_classes = (MultiPartParser,)\n def post(self, request):\n try:\n file = request.FILES[\"demo\"]\n logger.info(file.__dict__)\n res = parse_image(read_image(file.read()))\n serializer = FloorplanSerializer(\n data={\"title\": file.name, \"np_field\": res[\"b64\"].decode()}\n )", + "detail": "back.api.views", + "documentation": {} + }, + { + "label": "logger", + "kind": 5, + "importPath": "back.api.views", + "description": "back.api.views", + "peekOfCode": "logger = logging.getLogger(\"root\")\nclass Products(APIView):\n def get(self, request):\n tasks = Product.objects.all()\n serializer = ProductSerializer(tasks, many=True)\n return JsonResponse(serializer.data, safe=False)\n def post(self, request):\n data = JSONParser().parse(request)\n serializer = ProductSerializer(data=data)\n if serializer.is_valid():", "detail": "back.api.views", "documentation": {} }, @@ -8443,7 +8617,7 @@ "kind": 5, "importPath": "back.back.settings", "description": "back.back.settings", - "peekOfCode": "CSRF_TRUSTED_ORIGINS = (\n \"https://demo.kustarshina.ru\",\n \"http://localhost\",\n \"http://192.168.103.159\",\n)\nCORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:4173\",", + "peekOfCode": "CSRF_TRUSTED_ORIGINS = (\n \"https://demo.kustarshina.ru\",\n \"http://localhost\",\n \"http://localhost:3011\",\n \"http://192.168.103.159\",\n)\nCORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",", "detail": "back.back.settings", "documentation": {} }, @@ -8452,7 +8626,7 @@ "kind": 5, "importPath": "back.back.settings", "description": "back.back.settings", - "peekOfCode": "CORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",\n \"http://192.168.103.159\",\n \"http://192.168.103.159:8000\",", + "peekOfCode": "CORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:3000\",\n \"http://localhost:3011\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",", "detail": "back.back.settings", "documentation": {} }, @@ -8461,7 +8635,7 @@ "kind": 5, "importPath": "back.back.settings", "description": "back.back.settings", - "peekOfCode": "CORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",\n \"http://192.168.103.159\",\n \"http://192.168.103.159:8000\",\n \"http://front:4173\",", + "peekOfCode": "CORS_ORIGIN_WHITELIST = [\n \"null\",\n \"http://localhost\",\n \"http://localhost:3000\",\n \"http://localhost:3011\",\n \"http://localhost:4173\",\n \"http://localhost:5173\",\n \"http://localhost:8000\",\n \"http://127.0.0.1\",\n \"http://192.168.103.159\",", "detail": "back.back.settings", "documentation": {} }, @@ -8596,7 +8770,7 @@ "kind": 5, "importPath": "back.back.urls", "description": "back.back.urls", - "peekOfCode": "urlpatterns = [\n path(\"admin/\", admin.site.urls),\n path(\"api/products\", views.products),\n] + static('/files', document_root='files')", + "peekOfCode": "urlpatterns = [\n path(\"admin/\", admin.site.urls),\n path(\"api/products\", csrf_exempt(views.Products.as_view())),\n path(\"api/floorplan\", csrf_exempt(views.FloorplanView.as_view())),\n] + static('/files', document_root='files')", "detail": "back.back.urls", "documentation": {} }, diff --git a/back/api/admin.py b/back/api/admin.py index fd67800..6bb7c61 100644 --- a/back/api/admin.py +++ b/back/api/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin -from .models import Product +from .models import Product, Floorplan # Register your models here. admin.site.register(Product) +admin.site.register(Floorplan) diff --git a/back/api/models.py b/back/api/models.py index 9eec436..a9a1dde 100644 --- a/back/api/models.py +++ b/back/api/models.py @@ -20,3 +20,8 @@ class Product(models.Model): def __str__(self): return self.title + + +class Floorplan(models.Model): + title = models.CharField(max_length=200) + np_field = models.TextField() \ No newline at end of file diff --git a/back/api/serializers.py b/back/api/serializers.py index 46e721d..4fa38ab 100644 --- a/back/api/serializers.py +++ b/back/api/serializers.py @@ -1,5 +1,5 @@ from rest_framework import routers, serializers, viewsets -from .models import Product +from .models import Product, Floorplan class ProductSerializer(serializers.HyperlinkedModelSerializer): @@ -14,3 +14,11 @@ class ProductSerializer(serializers.HyperlinkedModelSerializer): "image2", "image3", ] + + +class FloorplanSerializer(serializers.Serializer): + title = serializers.CharField(max_length=200) + np_field = serializers.CharField() + + def create(self,validated_data): + return Floorplan.objects.create(**validated_data) diff --git a/back/api/tracer.py b/back/api/tracer.py index 18c39dd..872f8de 100644 --- a/back/api/tracer.py +++ b/back/api/tracer.py @@ -1,44 +1,66 @@ -from PIL import Image -from potrace import Bitmap, POTRACE_TURNPOLICY_MINORITY # `potracer` library +import io +import base64 +import cv2 +import numpy as np + +def numpy_arr_to_zip_str(arr): + f = io.BytesIO() + np.savez_compressed(f, arr=arr) + return base64.b64encode(f.getvalue()) + +def numpy_zip_str_to_arr(zip_str): + f = io.BytesIO(base64.b64decode(zip_str)) + return np.load(f)['arr'].tolist() + +def read_image(content: bytes) -> np.ndarray: + """ + Image bytes to OpenCV image + + :param content: Image bytes + :returns OpenCV image + :raises TypeError: If content is not bytes + :raises ValueError: If content does not represent an image + """ + if not isinstance(content, bytes): + raise TypeError(f"Expected 'content' to be bytes, received: {type(content)}") + image = cv2.imdecode(np.frombuffer(content, dtype=np.uint8), cv2.IMREAD_COLOR) + if image is None: + raise ValueError(f"Expected 'content' to be image bytes") + return image -def file_to_svg(filename: str): - try: +def parse_image(img): + (img_h, img_w) = img.shape[:2] + t = 1200 + w = t + h = int((img_h / img_w) * t) + img = cv2.resize(img, (w, h)) - image = Image.open(filename) - except IOError: - print("Image (%s) could not be loaded." % filename) - return - bm = Bitmap(image, blacklevel=0.5) - # bm.invert() - plist = bm.trace( - turdsize=2, - turnpolicy=POTRACE_TURNPOLICY_MINORITY, - alphamax=1, - opticurve=False, - opttolerance=0.2, - ) - with open(f"{filename}.svg", "w") as fp: - fp.write( - f'''''') - parts = [] - for curve in plist: - fs = curve.start_point - parts.append(f"M{fs.x},{fs.y}") - for segment in curve.segments: - if segment.is_corner: - a = segment.c - b = segment.end_point - parts.append(f"L{a.x},{a.y}L{b.x},{b.y}") - else: - a = segment.c1 - b = segment.c2 - c = segment.end_point - parts.append(f"C{a.x},{a.y} {b.x},{b.y} {c.x},{c.y}") - parts.append("z") - fp.write(f'') - fp.write("") + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray = 255 - gray + gray = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1] + gray = cv2.blur(gray, (10, 5)) + contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) + for cnt in contours: + area = cv2.contourArea(cnt) + # if area > 150000 and area < 500000: + cv2.drawContours(img, [cnt], 0, (255, 0, 0), 2) -if __name__ == '__main__': - file_to_svg('plan.png') \ No newline at end of file + svg_paths = [] + + for cnt in contours: + if len(cnt) > 80: + svg_path = "M" + for i in range(len(cnt)): + x, y = cnt[i][0] + svg_path += f"{x} {y} " + svg_paths.append(svg_path) + + return { + "width": w, + "height": h, + "paths": svg_paths, + "array": gray.tolist(), + "b64": numpy_arr_to_zip_str(gray), + } diff --git a/back/api/views.py b/back/api/views.py index a1dc6fb..f5fc7a3 100644 --- a/back/api/views.py +++ b/back/api/views.py @@ -1,21 +1,24 @@ -from django.shortcuts import render -from rest_framework.parsers import JSONParser -from django.views.decorators.csrf import csrf_exempt -from django.http import HttpResponse, JsonResponse -from .serializers import ProductSerializer -from .models import Product + +from rest_framework.parsers import JSONParser, MultiPartParser +from rest_framework.views import APIView +from django.http import JsonResponse + +from api.tracer import parse_image, read_image, numpy_zip_str_to_arr +from .serializers import FloorplanSerializer, ProductSerializer +from .models import Floorplan, Product + +import logging + +logger = logging.getLogger("root") -@csrf_exempt -def products(request): - """ - List all task snippets - """ - if request.method == "GET": +class Products(APIView): + def get(self, request): tasks = Product.objects.all() serializer = ProductSerializer(tasks, many=True) return JsonResponse(serializer.data, safe=False) - elif request.method == "POST": + + def post(self, request): data = JSONParser().parse(request) serializer = ProductSerializer(data=data) if serializer.is_valid(): @@ -23,3 +26,38 @@ def products(request): return JsonResponse(serializer.data, status=201) return JsonResponse(serializer.errors, status=400) + + +class FloorplanView(APIView): + + parser_classes = (MultiPartParser,) + + def post(self, request): + try: + file = request.FILES["demo"] + logger.info(file.__dict__) + res = parse_image(read_image(file.read())) + serializer = FloorplanSerializer( + data={"title": file.name, "np_field": res["b64"].decode()} + ) + logger.info(res["b64"]) + if serializer.is_valid(): + serializer.save() + return JsonResponse( + data={"response": {"array": res["array"]}}, status=201 + ) + return JsonResponse(serializer.errors, status=500) + except Exception as e: + logger.error(e) + raise e + + def get(self, request): + try: + item = Floorplan.objects.last() + serializer = FloorplanSerializer(item, many=False) + data = serializer.data + data["np_field"] = numpy_zip_str_to_arr(data["np_field"]) + return JsonResponse(data, safe=False) + except Exception as e: + logger.error(e) + raise e diff --git a/back/back/settings.py b/back/back/settings.py index afa0c4f..f579598 100644 --- a/back/back/settings.py +++ b/back/back/settings.py @@ -13,6 +13,7 @@ https://docs.djangoproject.com/en/5.0/ref/settings/ from pathlib import Path import os from dotenv import load_dotenv + load_dotenv() # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -42,17 +43,21 @@ ALLOWED_HOSTS = [ CSRF_TRUSTED_ORIGINS = ( "https://demo.kustarshina.ru", "http://localhost", + "http://localhost:3011", "http://192.168.103.159", ) CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = [ "null", "http://localhost", + "http://localhost:3000", + "http://localhost:3011", "http://localhost:4173", "http://localhost:5173", "http://localhost:8000", "http://127.0.0.1", "http://192.168.103.159", + "http://192.168.103.159:3000", "http://192.168.103.159:8000", "http://front:4173", "http://front:5173", diff --git a/back/back/urls.py b/back/back/urls.py index 767bc7b..ecf0181 100644 --- a/back/back/urls.py +++ b/back/back/urls.py @@ -18,8 +18,10 @@ from django.contrib import admin from django.urls import path from django.conf.urls.static import static from api import views +from django.views.decorators.csrf import csrf_exempt urlpatterns = [ path("admin/", admin.site.urls), - path("api/products", views.products), + path("api/products", csrf_exempt(views.Products.as_view())), + path("api/floorplan", csrf_exempt(views.FloorplanView.as_view())), ] + static('/files', document_root='files') diff --git a/front/components.d.ts b/front/components.d.ts index 7a13a85..611e1e7 100644 --- a/front/components.d.ts +++ b/front/components.d.ts @@ -7,6 +7,8 @@ export {} declare module 'vue' { export interface GlobalComponents { + copy: typeof import('./src/components/Home copy.vue')['default'] + Floorplan: typeof import('./src/components/Floorplan.vue')['default'] Game: typeof import('./src/components/Game.vue')['default'] Home: typeof import('./src/components/Home.vue')['default'] IMdi3dRotation: typeof import('~icons/mdi/3d-rotation')['default'] diff --git a/front/src/components/Floorplan.vue b/front/src/components/Floorplan.vue new file mode 100644 index 0000000..b2e2083 --- /dev/null +++ b/front/src/components/Floorplan.vue @@ -0,0 +1,17 @@ + + + \ No newline at end of file diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue index ce8579d..ddc1727 100644 --- a/front/src/components/Home.vue +++ b/front/src/components/Home.vue @@ -15,6 +15,9 @@
  • Игра
  • +
  • + План помещения и маршрут +
  • Табель рабочего времени
  • diff --git a/front/src/main.ts b/front/src/main.ts index c5fc888..405d6cd 100644 --- a/front/src/main.ts +++ b/front/src/main.ts @@ -8,11 +8,13 @@ import App from './App.vue' import Home from './components/Home.vue' import Projects from './components/Projects.vue' import Game from './components/Game.vue' +import Floorplan from './components/Floorplan.vue' const routes = [ { path: '/', component: Home }, { path: '/projects', component: Projects }, { path: '/game', component: Game }, + { path: '/floorplan', component: Floorplan }, ] const router = createRouter({ diff --git a/front/src/stores/floorplan.ts b/front/src/stores/floorplan.ts new file mode 100644 index 0000000..a59e6c6 --- /dev/null +++ b/front/src/stores/floorplan.ts @@ -0,0 +1,33 @@ +import { defineStore } from 'pinia' +import { SERVER_URL } from '../constants' + +export const useFloorplanStore = defineStore('floorplan', { + state: () => { + return { + // for initially empty lists + } + }, + actions: { + async getData() { + try { + const res = await fetch(`${SERVER_URL}/api/floorplan`) + const data = await res.json() + if (data.length) { + // this.list = data + } + } catch (error) { + // this.list = [] + } + }, + } +}) + +export interface ProductInfo { + id: number + title: string + description: string + model3d?: string + image1?: string + image2?: string + image3?: string +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index d8b7cf9..6c93c17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,13 +3,8 @@ name = "interactive-table" version = "0.1.0" description = "" authors = ["Your Name "] -<<<<<<< HEAD readme = "README.md" package-mode=false -======= -readme = "readme.md" -package-mode = false ->>>>>>> bx-434-game [tool.poetry.dependencies] python = "^3.10" @@ -19,13 +14,9 @@ taskipy = "^1.12.2" django-cors-headers = "^4.3.1" pillow = "^10.3.0" python-dotenv = "^1.0.1" -<<<<<<< HEAD psycopg2 = "^2.9.9" opencv-python = "^4.9.0.80" potracer = "^0.0.4" -======= -psycopg2-binary = "^2.9.9" ->>>>>>> bx-434-game [build-system]