# LaTiendaCOOP backend This README aims to document functionality of backend as well as required steps to get it up and running. ## Table of Contents - [First Steps](#first-steps) - [Load location data](#load-location-data) - [Load taxonomy data](#load-taxonomy-data) - [Social Auth](#social-auth) - [Company Endpoints](#company-endpoints) - [Product Endpoints](#product-endpoints) - [Core Endpoints](#core-endpoints) - [History Endpoints](#history-endpoints) - [Stats Endpoints](#stats-endpoints) - [Shop Integrations](#shop-integrations) - [WooCommerce](#woocommerce) - [Product Search](#product-search) - [Massive Data Load Endpoints](#massive-data-load-endpoints) - [COOP and Managing User Data Load](#coop-and-managing-user-data-load) - [Product Data Load](#product-data-load) - [GeoIP Setup](#geoip-setup) - [Tags](#tags) - [Development Utils](#development-utils) - [Fake product data generation](#fake-product-data-generation) ## First Steps - Clone repository: `git clone git@bitbucket.org:enreda/back-latienda.git` - Use docker image for Postgis ``` docker run --name postgis -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -d -p 5432:5432 postgis/postgis ``` If you are in ARM64 use the image `imresamu/postgis:17-3.5.2-alpine3.21` - Create file `.env` from `example.env` and populate fields correctly From inside the project's folder: - Make migrations: ``` python manage.py makemigrations core geo companies products history stats python manage.py migrate ``` - Start server in development mode: `python manage.py runserver` ### Load Location Data To load initial location data use: `python manage.py loadgisdata` ### Load Taxonomy Data This data serves as initial Tags To load initial set of tags: `python manage.py addtaxonomy` ## Social Auth Use your credentials from social media network to create acount and log in. Backend must be registered with every service that we want to use it with. During registration, provide a `Callback URL`, where the app can receive requests. You are given `client key` and `client secret` tokens, used to validate login requests. ### Facebook - Go to `developers.facebook.com/` - click on `My Apps` - On `Add a New App`, click on `Website` - Click on `Create App ID` - On the next screen click on `Skip Quick Start` - grab the `App ID` and `App Secret` - in `Settings / Basic`, click on the button `+ Add Platform` and add a website, and other info ## Company Endpoints ### CompanyViewSet Queryset: validated Company instances only Permissions: - anon user: safe methods - auth user: full access where user is company creator ### MyCompanyViewSet Queryset: Company instances where user is creator Permissions: - anon user: no access - auth user: full access ### AdminCompanyViewSet Queryset: all Company instances, validated or not Permissions: only accesible to authenticated users with role `SITE_ADMIN` ### random_company_sample Method view that returns a randome sample of companies By default it returns 6 instances, but can be customized through parameter `size` ## Product Endpoints ### ProductViewSet Endpoint url: `/api/v1/products/` Queryset: active Product instances only Permissions: - anon user: safe methods - auth user: full access where user is product creator ### MyProductsViewSet Endpoint url: `/api/v1/my_products/` Queryset: Product instances where user is creator Permissions: - anon user: no access - auth user: full access ### AdminProductsViewSet Endpoint url: `/api/v1/admin_products/` Queryset: all Product instances, acgtive or not Permissions: only accesible to authenticated users with role `SITE_ADMIN` ### load_coop_products [POST] Endpoint url: `/api/v1/load_products/` Method view that reads a CSV file. ### product_search [GET] Endpoint url: `/api/v1/search_products/` Allows searching of Products to all users Parameters: - q: used for search [MANDATORY] - limit: max number of returned instances [OPTIONAL] - offset: where to start counting results [OPTIONAL] - shipping_cost: true/false - discount: true/false - category: string - tags: string - order: string (newest/oldest) - price_min: int - price_max: int ### purchase_email [POST] Endpoint url: `/api/v1/purchase_email/` Sends email to company manager about the product that the user wants to purchase, and sends confirmation email to user. Parameters: - email: mandatory for anonymous users - telephone - company - product - comment ## Core Endpoints ### CustomUserViewSet Endpoint url: `/api/v1/users/` Queryset: all CustomUser instances Permissions: - anon user: only POST to register new user - auth user: no access - admin user: full access ### ChangeUserPasswordView Ednpoint url: `/api/v1/user/change_password//` Permissions: only accessible for your own user instance ### UpdateUserView Endpoint url: `/api/v1/user/update/` Permissions: only accessible for your own user instance ### create_company_user [POST] Edndpoint: `/api/v1/create_company_user/` Simultaneously create a company and its related user NOT WORKING!!! ### my_user [GET] Endpoint url: `/api/v1/my_user/` Returns instance of authenticated user ### load_coop_managers [POST] Ednpoint url: `/api/v1/load_coops/` For each row it creates a Company instance, and a user instance linked to the company, with role `COOP_MANAGER` ### activate_user Endpoint: `/activate///` This endpoint is reached from the URL sent to the user after their registration ### User Management Creation: - endpoint: `/api/v1/users/` - method: GET - payload: ```json { "email": "test@email.com", "full_name": "TEST NAME", "password": "VENTILADOR2ES1234499.89" } ``` Change password: - endpoint: api/v1/user/change_password/{user.pk}/ - method: POST - payload: ```json { "old_password": "my_old_password", "password": "SUPERSECRETNEWPASSWORD", "password2": "SUPERSECRETNEWPASSWORD" } ``` Update user profile: - endpoint: api/v1/users// - method: PUT - payload: ```json { "email": "new_user@email.com", "full_name": "Mr. TEST NAME" } ``` ### Authentication Implemented using `djangorestframework-simplejwt` New token pair endpoint: `/api/v1/token/` Response: ```json { "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYxMjI3MTcwNSwianRpIjoiZDU4YTgzYzFkYzFkNDI5MTljMGQ0NzcxNzljNzUxYTQiLCJ1c2VyX2lkIjo4fQ.yln80W5lONSyHwwqF4qBBHteqLuRfdLLWuaQANr_vxc", "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjEyMTg4OTA1LCJqdGkiOiIzNGIxMzM3NmU4MWI0OWY5YjU3ZmUxM2M5NThmZWZkYiIsInVzZXJfaWQiOjh9.aRDCUvKj7LCvixjPLC9ghy0h7rfRwR6Lo3A7HX4kSHE" } ``` Refresh expired token endpoint: `/api/v1/token/refresh/` ### Users Endpoint url: `/api/v1/users/` To get info on authenticated user: `/api/v1/my_user/` Authenticated users cannot create new users User are active by default To create user: ```json { "email": "test_user23@mail.com", "full_name": "Mr Test User", "password": "wqertewqr32qrewqr", "provider": "TWITTER" } ``` ## History Endpoints Endpoint url: `/api/v1/history/`: Historical records about product importation ## Stats Endpoints Endpoint url: `/api/v1/stats/` logs about user interaction with products links ## Location Endpoints Location ednpoints: - `/api/v1/countries/` - `/api/v1/regions/` - `/api/v1/provinces/` - `/api/v1/cities/` Tables filled with data from `datasets/gadm36_ESP.gpkg` with `loadgisdata` command. ## Shop Integrations We provide integrations with online shop platforms It requires the json field `Company.credentials` to have the appropiate format and values Endoint: `/api/v1/companies/{PK}/import_products/` The software to handle different platform imports can be found in `utils` ### WooCommerce Credential format: ```json { "key": "qwerweqr", "secret": "asdfsa" } ``` Method: `utils.woocommerce.migrate_shop_products` ## Product Search Endpoint: `/api/v1/product_search/` Query parameters: - `q`: text from the search input box Response format: ```json { "filters": { "tags": { "singles": ["tag1", "tag2"], // for tags that aren't nested "entry_1": ["subtag_1", "subtag_2"], // for tree tags like entry_1/subtag_1 "entry_2": ["subtag_1", "subtag_2"] // one per penultimate tag in tree }, "attributes": { "singles": ["tag1", "tag2"], // for tags that aren't nested "entry_1": ["subtag_1", "subtag_2"], // for tree tags like entry_1/subtag_1 "entry_2": ["subtag_1", "subtag_2"] // one per penultimate tag in tree } }, "products": [] // list of serialized instances, in order of relevancy } ``` Available query parameters: - q: used for search [MANDATORY] - limit: max number of returned instances [OPTIONAL] - offset: where to start counting results [OPTIONAL] - shipping_cost: true/false - discount: true/false - category: string - tags: string - order: string (newest/oldest) Check out `products.tests.ProductSearchTest` for a practical case. ## Massive Data Load Endpoints ### COOP and Managing User Data Load For massive load of data from COOPs and the managing user. CSV headers: `email,cif,nombre-coop,nombre-corto,url,es-tienda` Only admin users have access to endoint ### Product Data Load Endpoint: `/api/v1/load_products/` Only functional for users with a related company instance For massive load of product data. CSV headers: `sku,nombre-producto,descripcion,imagen,url,precio,gastos-envio,cond-envio,descuento,stock,tags,categoria,identificadores` Number fields must not include other symbols (like currency) ## GeoIP Setup Module: `geoip2` - Download the `GeoLite2 City` and `GeoLite2 Country` binary datasets from maxmind.com - Unzip files into `datasets/` folder - Set `settings.GEOIP_PATH` to datasets folder Optional: - install `libmaxminddb` C library for improved performance: `sudo apt install libmaxminddb0 libmaxminddb-dev mmdb-bin` ## Tags Both `Company` and `Product` models make use of tags. ### Load shopping taxonomy To create the initial set of tags, we can use the `addtaxonomy` management command. Reads the data from `datasets/shop-taxonomy.es-ES.txt` which is from google shopping ### Top-level tags In order to extract the top level tags for use as categories, we can use the `extractparenttas` management command. It saves the results to `datasets/top_tags.txt` ## Development Utils ### Fake product data generation To create a dataset of fake companies and products: `python manage.py addtestdata` Creates 10 Companies, with 10 products each. WARNING: the script deletes existing instances of both Company and Product