├── dress ├── __init__.py ├── core │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ ├── data.py │ │ ├── test_form_dress.py │ │ ├── test_model_dress.py │ │ ├── test_model_customer.py │ │ ├── test_form_customer.py │ │ ├── test_view_dress_list.py │ │ ├── test_view_customer_list.py │ │ ├── test_view_dress_detail.py │ │ └── test_view_customer_detail.py │ ├── apps.py │ ├── templates │ │ ├── core │ │ │ ├── dress_form.html │ │ │ ├── order_form.html │ │ │ ├── customer_form.html │ │ │ ├── order_list.html │ │ │ ├── dress_list.html │ │ │ ├── customer_list.html │ │ │ ├── order_detail.html │ │ │ ├── dress_detail.html │ │ │ └── customer_detail.html │ │ ├── pagination.html │ │ ├── footer.html │ │ ├── nav.html │ │ ├── base.html │ │ └── index.html │ ├── forms.py │ ├── serializers.py │ ├── static │ │ ├── css │ │ │ ├── main.css │ │ │ └── social.css │ │ └── js │ │ │ └── graphics │ │ │ ├── dress_color_chart.js │ │ │ ├── dress_size_chart.js │ │ │ ├── order_per_day_chart.js │ │ │ ├── person_size_chart.js │ │ │ └── order_value_chart.js │ ├── mixins.py │ ├── admin.py │ ├── urls.py │ ├── graphics.py │ ├── views.py │ └── models.py ├── selenium │ ├── selenium_screenshot.py │ ├── lists.py │ ├── gen_names.py │ ├── gen_random_values.py │ ├── selenium_dress.py │ └── selenium_customer.py ├── wsgi.py ├── utils │ └── lists.py ├── shell │ ├── shell_order.py │ ├── shell_dress.py │ └── shell_customer.py ├── urls.py └── settings.py ├── img └── graphics.png ├── contrib └── env-sample ├── manage.py ├── fix ├── dress.csv └── address.csv ├── requirements.txt ├── Makefile ├── .gitignore └── README.md /dress/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dress/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dress/core/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/graphics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rg3915/dg-challenge/master/img/graphics.png -------------------------------------------------------------------------------- /dress/core/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | name = 'core' 6 | -------------------------------------------------------------------------------- /contrib/env-sample: -------------------------------------------------------------------------------- 1 | SECRET_KEY=THIS_IS_NOT_A_GOOD_SECRET 2 | DEBUG=True 3 | ALLOWED_HOSTS=127.0.0.1, .localhost, .herokuapp.com 4 | -------------------------------------------------------------------------------- /dress/selenium/selenium_screenshot.py: -------------------------------------------------------------------------------- 1 | import time 2 | from selenium import webdriver 3 | 4 | page = webdriver.Firefox() 5 | page.maximize_window() 6 | time.sleep(0.5) 7 | page.get('http://localhost:8000/') 8 | time.sleep(1) 9 | page.save_screenshot('img/graphics.png') 10 | page.quit() 11 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dress.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /fix/dress.csv: -------------------------------------------------------------------------------- 1 | dress,color 2 | Athelas,preto 3 | Atlantis,branco 4 | Ester,prata 5 | Florbela green,verde 6 | Florbela pink,rosa 7 | Georgiana green,verde 8 | Georgiana,branco 9 | High line black,preto 10 | Jade black,preto 11 | Maddie,vermelho 12 | Mayara,marrom 13 | Noyon,branco 14 | Saint tropez,preto 15 | Suzane,vermelho -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dj-database-url==0.4.1 2 | Django==1.9.5 3 | django-bootstrap-form==3.2 4 | django-daterange-filter==1.2.0 5 | django-extensions==1.6.1 6 | django-localflavor==1.2 7 | django-widget-tweaks==1.4.1 8 | djangorestframework==3.3.3 9 | names==0.3.0 10 | python-decouple==3.0 11 | pytz==2016.4 12 | selenium==2.53.2 13 | six==1.10.0 14 | -------------------------------------------------------------------------------- /dress/selenium/lists.py: -------------------------------------------------------------------------------- 1 | COLORS = [ 2 | 'amarelo', 3 | 'azul', 4 | 'branco', 5 | 'cinza', 6 | 'coral', 7 | 'dourado', 8 | 'estampado', 9 | 'laranja', 10 | 'marrom', 11 | 'nude', 12 | 'prata', 13 | 'preto', 14 | 'rosa', 15 | 'roxo', 16 | 'verde', 17 | 'vermelho', 18 | 'vinho', 19 | ] 20 | -------------------------------------------------------------------------------- /dress/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for dress project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dress.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /dress/utils/lists.py: -------------------------------------------------------------------------------- 1 | COLORS = ( 2 | ('amarelo', 'amarelo'), 3 | ('azul', 'azul'), 4 | ('branco', 'branco'), 5 | ('cinza', 'cinza'), 6 | ('coral', 'coral'), 7 | ('dourado', 'dourado'), 8 | ('estampado', 'estampado'), 9 | ('laranja', 'laranja'), 10 | ('marrom', 'marrom'), 11 | ('nude', 'nude'), 12 | ('prata', 'prata'), 13 | ('preto', 'preto'), 14 | ('rosa', 'rosa'), 15 | ('roxo', 'roxo'), 16 | ('verde', 'verde'), 17 | ('vermelho', 'vermelho'), 18 | ('vinho', 'vinho'), 19 | ) 20 | -------------------------------------------------------------------------------- /dress/shell/shell_order.py: -------------------------------------------------------------------------------- 1 | import random 2 | from dress.core.models import Customer, Dress, Order 3 | 4 | REPEAT = random.randint(1, 20) 5 | customer = Customer.objects.all() 6 | dress = Dress.objects.all() 7 | 8 | for i in range(REPEAT): 9 | INDEX_CUSTOMER = random.randint(0, customer.count() - 1) 10 | INDEX_DRESS = random.randint(0, dress.count() - 1) 11 | Order.objects.create( 12 | customer=customer[INDEX_CUSTOMER], 13 | dress=dress[INDEX_DRESS], 14 | price=random.random() * random.randint(100, 1500) 15 | ) 16 | 17 | 18 | # done 19 | -------------------------------------------------------------------------------- /dress/core/templates/core/dress_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load bootstrap %} 4 | 5 | {% block title %}Dress Form{% endblock title %} 6 | 7 | {% block content %} 8 | 9 |
10 | Novo Vestido 11 | {% csrf_token %} 12 | {{ form|bootstrap_horizontal }} 13 | 14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | {% endblock content %} 22 | -------------------------------------------------------------------------------- /dress/core/templates/core/order_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load bootstrap %} 4 | 5 | {% block title %}Order Form{% endblock title %} 6 | 7 | {% block content %} 8 | 9 |
10 | Novo Pedido 11 | {% csrf_token %} 12 | {{ form|bootstrap_horizontal }} 13 | 14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | {% endblock content %} 22 | -------------------------------------------------------------------------------- /dress/core/templates/core/customer_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load bootstrap %} 4 | 5 | {% block title %}Customer Form{% endblock title %} 6 | 7 | {% block content %} 8 | 9 |
10 | Novo Cliente 11 | {% csrf_token %} 12 | {{ form|bootstrap_horizontal }} 13 | 14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | {% endblock content %} 22 | -------------------------------------------------------------------------------- /dress/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from django.contrib import admin 3 | from rest_framework import routers 4 | from .core import views 5 | 6 | router = routers.DefaultRouter() 7 | router.register(r'customers', views.CustomerViewSet) 8 | router.register(r'dresses', views.DressViewSet) 9 | router.register(r'orders', views.OrderViewSet) 10 | 11 | urlpatterns = [ 12 | url(r'^api/', include(router.urls)), 13 | url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), 14 | url(r'', include('dress.core.urls', namespace='core')), 15 | url(r'^admin/', admin.site.urls), 16 | ] 17 | -------------------------------------------------------------------------------- /dress/core/tests/data.py: -------------------------------------------------------------------------------- 1 | CUSTOMER_DICT = { 2 | 'first_name': 'Mariana', 3 | 'last_name': 'Cruz', 4 | 'email': 'm.cruz@example.com', 5 | 'phone': '98765-4321', 6 | 'address': 'Rua Sem Nome, 100', 7 | 'complement': 'apto 404', 8 | 'district': 'Santana', 9 | 'city': 'São Paulo', 10 | 'uf': 'SP', 11 | 'cep': '02035000', 12 | 'person_height': 1.7, 13 | 'bust': 38, 14 | 'hip': 40, 15 | 'waist': 38, 16 | 'heel': 36, 17 | 'person_size': 36 18 | } 19 | 20 | DRESS_DICT = { 21 | 'dress_model': 'Maddie', 22 | 'stylist': 'Roberto Cavalli', 23 | 'color': 'vermelho', 24 | 'dress_height': 1.55, 25 | 'dress_size': 36 26 | } 27 | -------------------------------------------------------------------------------- /fix/address.csv: -------------------------------------------------------------------------------- 1 | address,complement,district,city,uf,cep 2 | "Av. Ulisses Reis de Matos, 100",1º andar,Morumbi,São Paulo,SP,00568-602 3 | "Av Pedroso de Morais, 1552",,Pinheiros,São Paulo,SP,05420-002 4 | "Av 23 de Maio, 3041",,Vila Mariana,São Paulo,SP,04008-090 5 | "Rua Padre Machado, 68",10º andar,Vila Mariana,São Paulo,SP,04127-001 6 | "Rua Miguel, 337",casa 2,Jardim Novo Pantanal,São Paulo,SP,04472-060 7 | "Rua Colômbia, 659",,Jardim América,São Paulo,SP,01438-001 8 | "Av. Pacaembú, 380",apto 501,Barra Funda,São Paulo,SP,01155-000 9 | "Av. Indianópolis, 100",3º andar,Indianópolis,São Paulo,SP,04062-000 10 | "Rua Colômbia, 810",,Jardim América,São Paulo,SP,01438-001 11 | "Av. Moreira Guimarães, 367",fundos,Moema,São Paulo,SP,04074-030 12 | -------------------------------------------------------------------------------- /dress/core/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import Customer, Dress, Order 3 | 4 | 5 | class CustomerForm(forms.ModelForm): 6 | 7 | class Meta: 8 | model = Customer 9 | fields = ['first_name', 'last_name', 'email', 'phone', 10 | 'address', 'complement', 'district', 'city', 'uf', 'cep', 11 | 'person_height', 'bust', 'hip', 'waist', 'heel', 'person_size'] 12 | 13 | 14 | class DressForm(forms.ModelForm): 15 | 16 | class Meta: 17 | model = Dress 18 | fields = ['dress_model', 'stylist', 19 | 'color', 'dress_height', 'dress_size'] 20 | 21 | 22 | class OrderForm(forms.ModelForm): 23 | 24 | class Meta: 25 | model = Order 26 | fields = ['customer', 'dress', 'price'] 27 | -------------------------------------------------------------------------------- /dress/selenium/gen_names.py: -------------------------------------------------------------------------------- 1 | import random 2 | import names 3 | 4 | """ List of values for use in choices in models. """ 5 | treatment_male_list = ('Arq.', 'Dr.', 'Eng.', 'Prof.', 'Sr.',) 6 | 7 | treatment_female_list = ('Arqa.', 'Dona', u'Engª.', 'Profa.', 'Sra.', 'Srta.',) 8 | 9 | 10 | def gen_male_first_name(): 11 | treatment = random.choice(treatment_male_list) 12 | first_name = names.get_first_name(gender='male') 13 | return {'treatment': treatment, 'first_name': first_name} 14 | 15 | 16 | def gen_female_first_name(): 17 | treatment = random.choice(treatment_female_list) 18 | first_name = names.get_first_name(gender='female') 19 | return {'treatment': treatment, 'first_name': first_name} 20 | 21 | 22 | def gen_last_name(): 23 | return names.get_last_name() 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | migrate: 2 | python manage.py makemigrations core 3 | python manage.py migrate 4 | 5 | shell_customer: 6 | python manage.py shell < dress/shell/shell_customer.py 7 | 8 | shell_dress: 9 | python manage.py shell < dress/shell/shell_dress.py 10 | 11 | shell_order: 12 | python manage.py shell < dress/shell/shell_order.py 13 | 14 | selenium_customer: 15 | python dress/selenium/selenium_customer.py 16 | 17 | selenium_dress: 18 | python dress/selenium/selenium_dress.py 19 | 20 | selenium_screenshot: 21 | python dress/selenium/selenium_screenshot.py 22 | 23 | createuser: 24 | python manage.py createsuperuser --username='admin' --email='' 25 | 26 | backup: 27 | python manage.py dumpdata core --format=json --indent=2 > fixtures.json 28 | 29 | load: 30 | python manage.py loaddata fixtures.json 31 | -------------------------------------------------------------------------------- /dress/selenium/gen_random_values.py: -------------------------------------------------------------------------------- 1 | import string 2 | from random import randint, choice 3 | 4 | 5 | def gen_string(max_length): 6 | return str(''.join(choice(string.ascii_letters) for i in range(max_length))) 7 | gen_string.required = ['max_length'] 8 | 9 | 10 | def gen_number(min_number=1, max_number=99): 11 | # gera numeros inteiros entre 1 e 99 12 | return randint(min_number, max_number) 13 | 14 | 15 | def gen_digits(max_length): 16 | return str(''.join(choice(string.digits) for i in range(max_length))) 17 | 18 | 19 | def gen_phone(): 20 | # gera um telefone no formato xx xxxxx-xxxx 21 | digits_ = gen_digits(11) 22 | return '{} 9{}-{}'.format(digits_[:2], digits_[3:7], digits_[7:]) 23 | 24 | 25 | def gen_height(): 26 | a = randint(50, 99) 27 | return "1.%s" % a 28 | -------------------------------------------------------------------------------- /dress/core/tests/test_form_dress.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from dress.core.forms import DressForm 3 | from .data import DRESS_DICT 4 | 5 | 6 | class DressFormTest(TestCase): 7 | 8 | def test_form_has_fields(self): 9 | ''' Form must have 5 fields ''' 10 | form = DressForm() 11 | expected = ['dress_model', 'stylist', 12 | 'color', 'dress_height', 'dress_size'] 13 | self.assertSequenceEqual(expected, list(form.fields)) 14 | 15 | def assertFormErrorMessage(self, form, field, msg): 16 | errors = form.errors 17 | errors_list = errors[field] 18 | self.assertListEqual([msg], errors_list) 19 | 20 | def make_validated_form(self, **kwargs): 21 | data = dict(**DRESS_DICT, **kwargs) 22 | form = DressForm(data) 23 | form.is_valid() 24 | return form 25 | -------------------------------------------------------------------------------- /dress/core/templates/pagination.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 19 |
20 |
21 | 22 | -------------------------------------------------------------------------------- /dress/core/tests/test_model_dress.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from django.test import TestCase 3 | from django.shortcuts import resolve_url as r 4 | from dress.core.models import Dress 5 | from .data import DRESS_DICT 6 | 7 | 8 | class DressModelTest(TestCase): 9 | 10 | def setUp(self): 11 | self.obj = Dress(**DRESS_DICT) 12 | self.obj.save() 13 | 14 | def test_create(self): 15 | self.assertTrue(Dress.objects.exists()) 16 | 17 | def test_created_at(self): 18 | ''' Dress must have an auto created_at attr. ''' 19 | self.assertIsInstance(self.obj.created, datetime) 20 | 21 | def test_str(self): 22 | self.assertEqual('Maddie', str(self.obj)) 23 | 24 | def test_get_absolute_url(self): 25 | url = r('core:dress_detail', self.obj.pk) 26 | self.assertEqual(url, self.obj.get_absolute_url()) 27 | -------------------------------------------------------------------------------- /dress/core/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Customer, Dress, Order 3 | 4 | 5 | class CustomerSerializer(serializers.ModelSerializer): 6 | 7 | class Meta: 8 | model = Customer 9 | fields = ('first_name', 'last_name', 'email', 'phone', 10 | 'address', 'complement', 'district', 'city', 'uf', 'cep', 11 | 'person_height', 'bust', 'hip', 'waist', 'heel', 12 | 'person_size') 13 | 14 | 15 | class DressSerializer(serializers.ModelSerializer): 16 | 17 | class Meta: 18 | model = Dress 19 | fields = ('dress_model', 'stylist', 'color', 20 | 'dress_height', 'dress_size') 21 | 22 | 23 | class OrderSerializer(serializers.ModelSerializer): 24 | 25 | class Meta: 26 | model = Order 27 | fields = ('customer', 'dress', 'price') 28 | -------------------------------------------------------------------------------- /dress/core/tests/test_model_customer.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from django.test import TestCase 3 | from django.shortcuts import resolve_url as r 4 | from dress.core.models import Customer 5 | from .data import CUSTOMER_DICT 6 | 7 | 8 | class CustomerModelTest(TestCase): 9 | 10 | def setUp(self): 11 | self.obj = Customer(**CUSTOMER_DICT) 12 | self.obj.save() 13 | 14 | def test_create(self): 15 | self.assertTrue(Customer.objects.exists()) 16 | 17 | def test_created_at(self): 18 | ''' Customer must have an auto created_at attr. ''' 19 | self.assertIsInstance(self.obj.created, datetime) 20 | 21 | def test_str(self): 22 | self.assertEqual('Mariana Cruz', str(self.obj)) 23 | 24 | def test_get_absolute_url(self): 25 | url = r('core:customer_detail', self.obj.pk) 26 | self.assertEqual(url, self.obj.get_absolute_url()) 27 | -------------------------------------------------------------------------------- /dress/core/templates/footer.html: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /dress/core/tests/test_form_customer.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from dress.core.forms import CustomerForm 3 | from .data import CUSTOMER_DICT 4 | 5 | 6 | class CustomerFormTest(TestCase): 7 | 8 | def test_form_has_fields(self): 9 | ''' Form must have 16 fields ''' 10 | form = CustomerForm() 11 | expected = ['first_name', 'last_name', 'email', 'phone', 12 | 'address', 'complement', 'district', 'city', 'uf', 'cep', 13 | 'person_height', 'bust', 'hip', 'waist', 'heel', 14 | 'person_size'] 15 | self.assertSequenceEqual(expected, list(form.fields)) 16 | 17 | def assertFormErrorMessage(self, form, field, msg): 18 | errors = form.errors 19 | errors_list = errors[field] 20 | self.assertListEqual([msg], errors_list) 21 | 22 | def make_validated_form(self, **kwargs): 23 | data = dict(**CUSTOMER_DICT, **kwargs) 24 | form = CustomerForm(data) 25 | form.is_valid() 26 | return form 27 | -------------------------------------------------------------------------------- /dress/core/static/css/main.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | /* http://getbootstrap.com/examples/sticky-footer-navbar/sticky-footer-navbar.css */ 4 | /* http://getbootstrap.com/2.3.2/examples/sticky-footer.html */ 5 | html { 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | body { 10 | /* Margin bottom by footer height */ 11 | margin-bottom: 60px; 12 | } 13 | #footer { 14 | position: absolute; 15 | bottom: 0; 16 | width: 100%; 17 | /* Set the fixed height of the footer here */ 18 | height: 60px; 19 | background-color: #101010; 20 | } 21 | .credit { 22 | /* Center vertical text */ 23 | margin: 20px 0; 24 | } 25 | /* Lastly, apply responsive CSS fixes as necessary */ 26 | @media (max-width: 767px) { 27 | body { 28 | margin-bottom: 120px; 29 | } 30 | 31 | #footer { 32 | height: 120px; 33 | padding-left: 5px; 34 | padding-right: 5px; 35 | } 36 | } 37 | /* My personal styles. */ 38 | .ok { 39 | color: #44AD41; /*verde*/ 40 | } 41 | 42 | .no { 43 | color: #DE2121; /*vermelho*/ 44 | } 45 | -------------------------------------------------------------------------------- /dress/core/templates/nav.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | -------------------------------------------------------------------------------- /dress/core/tests/test_view_dress_list.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.shortcuts import resolve_url as r 3 | from dress.core.models import Dress 4 | from .data import DRESS_DICT 5 | 6 | 7 | class DressListGet(TestCase): 8 | 9 | def setUp(self): 10 | self.obj = Dress.objects.create(**DRESS_DICT) 11 | self.resp = self.client.get(r('core:dress_list')) 12 | 13 | def test_get(self): 14 | self.assertEqual(200, self.resp.status_code) 15 | 16 | def test_template(self): 17 | self.assertTemplateUsed(self.resp, 'core/dress_list.html') 18 | 19 | def test_html(self): 20 | contents = [ 21 | (1, 'Maddie'), 22 | (1, 'Roberto Cavalli'), 23 | ] 24 | 25 | for count, expected in contents: 26 | with self.subTest(): 27 | self.assertContains(self.resp, expected, count) 28 | 29 | 30 | class DressGetEmpty(TestCase): 31 | 32 | def test_get_empty(self): 33 | response = self.client.get(r('core:dress_list')) 34 | 35 | self.assertContains(response, 'Sem itens na lista.') 36 | -------------------------------------------------------------------------------- /dress/shell/shell_dress.py: -------------------------------------------------------------------------------- 1 | import random 2 | import csv 3 | from dress.core.models import Dress 4 | from dress.selenium.gen_names import gen_female_first_name, gen_male_first_name, gen_last_name 5 | from dress.selenium.gen_random_values import gen_number, gen_height 6 | 7 | dress_list = [] 8 | 9 | ''' Lendo os dados de dress.csv ''' 10 | with open('fix/dress.csv', 'r') as f: 11 | r = csv.DictReader(f) 12 | for dct in r: 13 | dress_list.append(dct) 14 | f.close() 15 | 16 | 17 | REPEAT = random.randint(1, 20) 18 | 19 | ''' Insert Dress ''' 20 | for i in range(REPEAT): 21 | g = random.randint(0, 1) 22 | dict_ = gen_female_first_name() if g else gen_male_first_name() 23 | first_name = dict_['first_name'] 24 | last_name = gen_last_name() 25 | full_name = ' '.join([first_name, last_name]) 26 | print(full_name) 27 | INDEX = random.randint(0, 13) 28 | obj = Dress( 29 | dress_model=dress_list[INDEX]['dress'], 30 | stylist=full_name, 31 | color=dress_list[INDEX]['color'], 32 | dress_height=gen_height(), 33 | dress_size=gen_number(34, 44), 34 | ) 35 | obj.save() 36 | 37 | 38 | # Done 39 | -------------------------------------------------------------------------------- /dress/core/tests/test_view_customer_list.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.shortcuts import resolve_url as r 3 | from dress.core.models import Customer 4 | from .data import CUSTOMER_DICT 5 | 6 | 7 | class CustomerListGet(TestCase): 8 | 9 | def setUp(self): 10 | self.obj = Customer.objects.create(**CUSTOMER_DICT) 11 | self.resp = self.client.get(r('core:customer_list')) 12 | 13 | def test_get(self): 14 | self.assertEqual(200, self.resp.status_code) 15 | 16 | def test_template(self): 17 | self.assertTemplateUsed(self.resp, 'core/customer_list.html') 18 | 19 | def test_html(self): 20 | contents = [ 21 | (1, 'Mariana Cruz'), 22 | (1, 'm.cruz@example.com'), 23 | (2, 'São Paulo'), 24 | ] 25 | 26 | for count, expected in contents: 27 | with self.subTest(): 28 | self.assertContains(self.resp, expected, count) 29 | 30 | 31 | class CustomerGetEmpty(TestCase): 32 | 33 | def test_get_empty(self): 34 | response = self.client.get(r('core:customer_list')) 35 | 36 | self.assertContains(response, 'Sem itens na lista.') 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | 64 | __pycache__/ 65 | *.py[cod] 66 | *.sqlite3 67 | *.env 68 | *.DS_Store 69 | .venv/ 70 | staticfiles/ 71 | .ipynb_checkpoints/ 72 | 73 | dress/core/migrations/ 74 | -------------------------------------------------------------------------------- /dress/core/mixins.py: -------------------------------------------------------------------------------- 1 | from django.db.models import Q 2 | 3 | 4 | class NameSearchMixin(object): 5 | 6 | def get_queryset(self): 7 | queryset = super(NameSearchMixin, self).get_queryset() 8 | q = self.request.GET.get('search_box') 9 | if q: 10 | return queryset.filter( 11 | Q(first_name__icontains=q) | 12 | Q(last_name__icontains=q) | 13 | Q(email__icontains=q)) 14 | return queryset 15 | 16 | 17 | class DressSearchMixin(object): 18 | 19 | def get_queryset(self): 20 | queryset = super(DressSearchMixin, self).get_queryset() 21 | q = self.request.GET.get('search_box') 22 | if q: 23 | return queryset.filter( 24 | Q(dress_model__icontains=q) | 25 | Q(stylist__icontains=q)) 26 | return queryset 27 | 28 | 29 | class OrderSearchMixin(object): 30 | 31 | def get_queryset(self): 32 | queryset = super(OrderSearchMixin, self).get_queryset() 33 | q = self.request.GET.get('search_box') 34 | if q: 35 | return queryset.filter( 36 | Q(customer__first_name__icontains=q) | 37 | Q(dress__dress_model__icontains=q)) 38 | return queryset 39 | -------------------------------------------------------------------------------- /dress/core/tests/test_view_dress_detail.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.shortcuts import resolve_url as r 3 | from dress.core.models import Dress 4 | from .data import DRESS_DICT 5 | 6 | 7 | class DressDetailGet(TestCase): 8 | 9 | def setUp(self): 10 | self.obj = Dress.objects.create(**DRESS_DICT) 11 | self.resp = self.client.get(r('core:dress_detail', self.obj.pk)) 12 | 13 | def test_get(self): 14 | self.assertEqual(200, self.resp.status_code) 15 | 16 | def test_template(self): 17 | self.assertTemplateUsed( 18 | self.resp, 'core/dress_detail.html') 19 | 20 | def test_context(self): 21 | dress = self.resp.context['dress'] 22 | self.assertIsInstance(dress, Dress) 23 | 24 | def test_html(self): 25 | contents = (self.obj.dress_model, 26 | self.obj.stylist, 27 | self.obj.color, 28 | self.obj.dress_size, 29 | ) 30 | 31 | with self.subTest(): 32 | for expected in contents: 33 | self.assertContains(self.resp, expected) 34 | 35 | 36 | class DressDetailNotFound(TestCase): 37 | 38 | def test_not_found(self): 39 | resp = self.client.get(r('core:dress_detail', 0)) 40 | self.assertEqual(404, resp.status_code) 41 | -------------------------------------------------------------------------------- /dress/core/admin.py: -------------------------------------------------------------------------------- 1 | from daterange_filter.filter import DateRangeFilter 2 | from django.contrib import admin 3 | from .models import Customer, Dress, Order 4 | from .forms import CustomerForm, DressForm, OrderForm 5 | 6 | 7 | @admin.register(Customer) 8 | class CustomerAdmin(admin.ModelAdmin): 9 | list_display = ('__str__', 'email', 'phone', 'city', 'uf', 10 | 'person_height', 'person_size', 'created') 11 | date_hierarchy = 'created' 12 | search_fields = ('first_name', 'last_name', 'email') 13 | list_filter = ( 14 | # 'uf', 15 | ('created', DateRangeFilter), 16 | ) 17 | form = CustomerForm 18 | 19 | 20 | @admin.register(Dress) 21 | class DressAdmin(admin.ModelAdmin): 22 | list_display = ('dress_model', 'stylist', 'color', 23 | 'dress_height', 'dress_size', 'created') 24 | date_hierarchy = 'created' 25 | search_fields = ('dress_model', 'stylist') 26 | list_filter = ( 27 | 'color', 28 | ('created', DateRangeFilter), 29 | ) 30 | form = DressForm 31 | 32 | 33 | @admin.register(Order) 34 | class OrderAdmin(admin.ModelAdmin): 35 | list_display = ('id', 'customer', 'dress', 'price', 'created') 36 | date_hierarchy = 'created' 37 | search_fields = ('customer', 'dress') 38 | list_filter = ( 39 | ('created', DateRangeFilter), 40 | ) 41 | form = OrderForm 42 | -------------------------------------------------------------------------------- /dress/selenium/selenium_dress.py: -------------------------------------------------------------------------------- 1 | import time 2 | import csv 3 | from random import randint 4 | from selenium import webdriver 5 | from gen_names import gen_female_first_name, gen_male_first_name, gen_last_name 6 | from gen_random_values import gen_number, gen_height 7 | 8 | page = webdriver.Firefox() 9 | page.maximize_window() 10 | time.sleep(0.5) 11 | page.get('http://localhost:8000/dress/add/') 12 | 13 | g = randint(0, 1) 14 | 15 | if g: 16 | dict_ = gen_female_first_name() 17 | else: 18 | dict_ = gen_male_first_name() 19 | 20 | first_name = dict_['first_name'] 21 | last_name = gen_last_name() 22 | full_name = ' '.join([first_name, last_name]) 23 | print(full_name) 24 | 25 | dress_list = [] 26 | 27 | ''' Lendo os dados de dress.csv ''' 28 | with open('fix/dress.csv', 'r') as f: 29 | r = csv.DictReader(f) 30 | for dct in r: 31 | dress_list.append(dct) 32 | f.close() 33 | 34 | INDEX = randint(0, 13) 35 | 36 | fields = [ 37 | ['id_dress_model', dress_list[INDEX]['dress']], 38 | ['id_stylist', full_name], 39 | ['id_color', dress_list[INDEX]['color']], 40 | ['id_dress_height', gen_height()], 41 | ['id_dress_size', gen_number(34, 44)], 42 | ] 43 | 44 | for field in fields: 45 | search = page.find_element_by_id(field[0]) 46 | search.send_keys(field[1]) 47 | 48 | # button = page.find_element_by_id('id_submit') 49 | button = page.find_element_by_class_name('btn-primary') 50 | button.click() 51 | 52 | page.quit() 53 | -------------------------------------------------------------------------------- /dress/core/static/js/graphics/dress_color_chart.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | var url = "/dresses/dress_per_color_json/"; 3 | 4 | $.getJSON(url, function(res){ 5 | console.log(res); 6 | /* Transformando o dicionário em lista. 7 | Com o comando map eu coloco uma lista dentro da outra, 8 | necessário para este tipo de gráfico. */ 9 | var data = res.map(function (v) { 10 | return [v.cor, v.quant] 11 | }); 12 | 13 | console.log(data); 14 | 15 | $('#dress_color_chart').highcharts({ 16 | chart: { 17 | type: 'bar' 18 | }, 19 | title: { 20 | text: '' 21 | }, 22 | xAxis: { 23 | type: 'category' 24 | }, 25 | yAxis: { 26 | min: 0, 27 | title: { 28 | text: 'Quantidade' 29 | } 30 | }, 31 | legend: { 32 | enabled: false 33 | }, 34 | // colors: ['#44AD41', '#DE2121'], 35 | series: [{ 36 | data: data, 37 | colorByPoint: true, 38 | dataLabels: { 39 | enabled: true, 40 | align: 'center', 41 | color: '#FFFFFF', 42 | x: -25, // 25 pixels down from the top 43 | style: { 44 | fontSize: '15px' 45 | } 46 | } 47 | }], 48 | }); 49 | }); 50 | }); -------------------------------------------------------------------------------- /dress/core/static/js/graphics/dress_size_chart.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | var url = "/dresses/dress_per_size_json/"; 3 | 4 | $.getJSON(url, function(res){ 5 | console.log(res); 6 | /* Transformando o dicionário em lista. 7 | Com o comando map eu coloco uma lista dentro da outra, 8 | necessário para este tipo de gráfico. */ 9 | var data = res.map(function (v) { 10 | return [v.tamanho, v.quant] 11 | }); 12 | 13 | console.log(data); 14 | 15 | $('#dress_size_chart').highcharts({ 16 | chart: { 17 | type: 'column' 18 | }, 19 | title: { 20 | text: '' 21 | }, 22 | xAxis: { 23 | type: 'category' 24 | }, 25 | yAxis: { 26 | min: 0, 27 | title: { 28 | text: 'Quantidade' 29 | } 30 | }, 31 | legend: { 32 | enabled: false 33 | }, 34 | // colors: ['#44AD41', '#DE2121'], 35 | series: [{ 36 | data: data, 37 | colorByPoint: true, 38 | dataLabels: { 39 | enabled: true, 40 | align: 'center', 41 | color: '#FFFFFF', 42 | y: 25, // 25 pixels down from the top 43 | style: { 44 | fontSize: '15px' 45 | } 46 | } 47 | }], 48 | }); 49 | }); 50 | }); -------------------------------------------------------------------------------- /dress/core/static/js/graphics/order_per_day_chart.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | var url = "/orders/order_per_day_json/"; 3 | 4 | $.getJSON(url, function(res){ 5 | console.log(res); 6 | /* Transformando o dicionário em lista. 7 | Com o comando map eu coloco uma lista dentro da outra, 8 | necessário para este tipo de gráfico. */ 9 | var data = res.map(function (v) { 10 | return [v.dia, v.quant] 11 | }); 12 | 13 | console.log(data); 14 | 15 | $('#order_per_day_chart').highcharts({ 16 | chart: { 17 | type: 'column' 18 | }, 19 | title: { 20 | text: '' 21 | }, 22 | xAxis: { 23 | type: 'category' 24 | }, 25 | yAxis: { 26 | min: 0, 27 | title: { 28 | text: 'Quantidade' 29 | } 30 | }, 31 | legend: { 32 | enabled: false 33 | }, 34 | // colors: ['#44AD41', '#DE2121'], 35 | series: [{ 36 | data: data, 37 | colorByPoint: true, 38 | dataLabels: { 39 | enabled: true, 40 | align: 'center', 41 | color: '#FFFFFF', 42 | y: 25, // 25 pixels down from the top 43 | style: { 44 | fontSize: '15px' 45 | } 46 | } 47 | }], 48 | }); 49 | }); 50 | }); -------------------------------------------------------------------------------- /dress/core/static/js/graphics/person_size_chart.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | var url = "/customers/customer_per_size_json/"; 3 | 4 | $.getJSON(url, function(res){ 5 | console.log(res); 6 | /* Transformando o dicionário em lista. 7 | Com o comando map eu coloco uma lista dentro da outra, 8 | necessário para este tipo de gráfico. */ 9 | var data = res.map(function (v) { 10 | return [v.tamanho, v.quant] 11 | }); 12 | 13 | console.log(data); 14 | 15 | $('#person_size_chart').highcharts({ 16 | chart: { 17 | type: 'column' 18 | }, 19 | title: { 20 | text: '' 21 | }, 22 | xAxis: { 23 | type: 'category' 24 | }, 25 | yAxis: { 26 | min: 0, 27 | title: { 28 | text: 'Quantidade' 29 | } 30 | }, 31 | legend: { 32 | enabled: false 33 | }, 34 | // colors: ['#44AD41', '#DE2121'], 35 | series: [{ 36 | data: data, 37 | colorByPoint: true, 38 | dataLabels: { 39 | enabled: true, 40 | align: 'center', 41 | color: '#FFFFFF', 42 | y: 25, // 25 pixels down from the top 43 | style: { 44 | fontSize: '15px' 45 | } 46 | } 47 | }], 48 | }); 49 | }); 50 | }); -------------------------------------------------------------------------------- /dress/shell/shell_customer.py: -------------------------------------------------------------------------------- 1 | import random 2 | import csv 3 | from dress.core.models import Customer 4 | from dress.selenium.gen_names import gen_female_first_name, gen_last_name 5 | from dress.selenium.gen_random_values import gen_number, gen_phone, gen_height 6 | 7 | address_list = [] 8 | 9 | ''' Lendo os dados de address.csv ''' 10 | with open('fix/address.csv', 'r') as f: 11 | r = csv.DictReader(f) 12 | for dct in r: 13 | address_list.append(dct) 14 | f.close() 15 | 16 | REPEAT = random.randint(1, 20) 17 | 18 | ''' Insert Customers ''' 19 | for i in range(REPEAT): 20 | dict_ = gen_female_first_name() 21 | first_name = dict_['first_name'] 22 | last_name = gen_last_name() 23 | print(first_name, last_name) 24 | email = '{}.{}@example.com'.format( 25 | first_name[0].lower(), last_name.lower()) 26 | INDEX = random.randint(0, 9) 27 | obj = Customer( 28 | first_name=first_name, 29 | last_name=last_name, 30 | email=email, 31 | phone=gen_phone(), 32 | address=address_list[INDEX]['address'], 33 | complement=address_list[INDEX]['complement'], 34 | district=address_list[INDEX]['district'], 35 | city=address_list[INDEX]['city'], 36 | uf=address_list[INDEX]['city'], 37 | cep=address_list[INDEX]['cep'], 38 | person_height=gen_height(), 39 | bust=gen_number(34, 42), 40 | hip=gen_number(34, 42), 41 | waist=gen_number(34, 44), 42 | heel=gen_number(34, 44), 43 | person_size=gen_number(34, 44), 44 | ) 45 | obj.save() 46 | 47 | 48 | # Done 49 | -------------------------------------------------------------------------------- /dress/core/static/js/graphics/order_value_chart.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | var url = "/orders/order_value_json/"; 3 | 4 | $.getJSON(url, function(res){ 5 | console.log(res); 6 | /* Transformando o dicionário em lista. 7 | Com o comando map eu coloco uma lista dentro da outra, 8 | necessário para este tipo de gráfico. */ 9 | var data = res.map(function (v) { 10 | return [v.id, v.price] 11 | }); 12 | 13 | console.log(data); 14 | 15 | $('#order_value_chart').highcharts({ 16 | chart: { 17 | type: 'line' 18 | }, 19 | title: { 20 | text: '' 21 | }, 22 | xAxis: { 23 | type: 'category' 24 | }, 25 | yAxis: { 26 | min: 0, 27 | title: { 28 | text: 'Preço' 29 | }, 30 | plotOptions: { 31 | line: { 32 | dataLabels: { 33 | enabled: true 34 | }, 35 | } 36 | }, 37 | }, 38 | legend: { 39 | enabled: false 40 | }, 41 | series: [{ 42 | data: data, 43 | dataLabels: { 44 | enabled: true, 45 | align: 'center', 46 | style: { 47 | fontSize: '15px' 48 | } 49 | } 50 | }], 51 | }); 52 | }); 53 | }); -------------------------------------------------------------------------------- /dress/core/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% block title %}Django{% endblock title %} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | {% include "nav.html" %} 39 |
40 |
41 | {% block content %}{% endblock content %} 42 | {% block js %}{% endblock js %} 43 |
44 |
45 | {% include "footer.html" %} 46 | 47 | 48 | -------------------------------------------------------------------------------- /dress/core/tests/test_view_customer_detail.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.shortcuts import resolve_url as r 3 | from dress.core.models import Customer 4 | from .data import CUSTOMER_DICT 5 | 6 | 7 | class CustomerDetailGet(TestCase): 8 | 9 | def setUp(self): 10 | self.obj = Customer.objects.create(**CUSTOMER_DICT) 11 | self.resp = self.client.get(r('core:customer_detail', self.obj.pk)) 12 | 13 | def test_get(self): 14 | self.assertEqual(200, self.resp.status_code) 15 | 16 | def test_template(self): 17 | self.assertTemplateUsed( 18 | self.resp, 'core/customer_detail.html') 19 | 20 | def test_context(self): 21 | customer = self.resp.context['customer'] 22 | self.assertIsInstance(customer, Customer) 23 | 24 | def test_html(self): 25 | contents = (self.obj.full_name, 26 | self.obj.email, 27 | self.obj.phone, 28 | self.obj.address, 29 | self.obj.complement, 30 | self.obj.district, 31 | self.obj.city, 32 | self.obj.uf, 33 | self.obj.cep, 34 | self.obj.bust, 35 | self.obj.hip, 36 | self.obj.waist, 37 | self.obj.heel, 38 | self.obj.person_size, 39 | ) 40 | 41 | with self.subTest(): 42 | for expected in contents: 43 | self.assertContains(self.resp, expected) 44 | 45 | 46 | class CustomerDetailNotFound(TestCase): 47 | 48 | def test_not_found(self): 49 | resp = self.client.get(r('core:customer_detail', 0)) 50 | self.assertEqual(404, resp.status_code) 51 | -------------------------------------------------------------------------------- /dress/core/templates/core/order_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Pedidos{% endblock title %} 4 | 5 | {% block content %} 6 | 7 | 20 | 21 | 24 | 25 |
26 | {% if order_list %} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% for order in order_list %} 38 | 39 | 40 | 41 | 42 | 43 | 44 | {% endfor %} 45 | 46 |
#ClienteVestidoAluguel
{{ order.id }}{{ order.customer }}{{ order.dress }}R$ {{ order.price }}
47 | {% else %} 48 |

Sem itens na lista.

49 | {% endif %} 50 |
51 | 52 |
53 |
54 |

Total: {{ page_obj.paginator.count }} pedido{{ page_obj.paginator.count|pluralize }}

55 |
56 | 57 | {% include "pagination.html" %} 58 | 59 | {% endblock content %} 60 | -------------------------------------------------------------------------------- /dress/core/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | from dress.core import views as c 3 | from dress.core import graphics as g 4 | 5 | customers_patterns = [ 6 | url(r'^$', c.CustomerList.as_view(), name='customer_list'), 7 | url(r'^add/$', c.customer_create, name='customer_add'), 8 | url(r'^(?P\d+)/$', c.customer_detail, name='customer_detail'), 9 | url(r'^(?P\d+)/edit/$', c.customer_update, name='customer_edit'), 10 | url(r'^(?P\d+)/delete/$', c.customer_delete, name='customer_delete'), 11 | url(r'^customer_per_size_json/$', g.customer_per_size_json), 12 | ] 13 | 14 | dresses_patterns = [ 15 | url(r'^$', c.DressList.as_view(), name='dress_list'), 16 | url(r'^add/$', c.dress_create, name='dress_add'), 17 | url(r'^(?P\d+)/$', c.dress_detail, name='dress_detail'), 18 | url(r'^(?P\d+)/edit/$', c.dress_update, name='dress_edit'), 19 | url(r'^(?P\d+)/delete/$', c.dress_delete, name='dress_delete'), 20 | url(r'^dress_per_color_json/$', g.dress_per_color_json), 21 | url(r'^dress_per_size_json/$', g.dress_per_size_json), 22 | ] 23 | 24 | orders_patterns = [ 25 | url(r'^$', c.OrderList.as_view(), name='order_list'), 26 | url(r'^add/$', c.order_create, name='order_add'), 27 | url(r'^(?P\d+)/$', c.order_detail, name='order_detail'), 28 | url(r'^(?P\d+)/edit/$', c.order_update, name='order_edit'), 29 | url(r'^(?P\d+)/delete/$', c.order_delete, name='order_delete'), 30 | url(r'^order_per_day_json/$', g.order_per_day_json), 31 | url(r'^order_value_json/$', g.order_value_json), 32 | ] 33 | 34 | urlpatterns = [ 35 | url(r'^$', c.home, name='home'), 36 | url(r'^customers/', include(customers_patterns)), 37 | url(r'^dresses/', include(dresses_patterns)), 38 | url(r'^orders/', include(orders_patterns)), 39 | ] 40 | -------------------------------------------------------------------------------- /dress/core/templates/core/dress_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Vestidos{% endblock title %} 4 | 5 | {% block content %} 6 | 7 | 20 | 21 | 24 | 25 |
26 | {% if dress_list %} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% for dress in dress_list %} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {% endfor %} 47 | 48 |
ModeloEstilistaCorAlturaTamanho
{{ dress.dress_model }}{{ dress.stylist }}{{ dress.color }}{{ dress.dress_height }}{{ dress.dress_size }}
49 | {% else %} 50 |

Sem itens na lista.

51 | {% endif %} 52 |
53 | 54 |
55 |
56 |

Total: {{ page_obj.paginator.count }} vestido{{ page_obj.paginator.count|pluralize }}

57 |
58 | 59 | {% include "pagination.html" %} 60 | 61 | {% endblock content %} 62 | -------------------------------------------------------------------------------- /dress/selenium/selenium_customer.py: -------------------------------------------------------------------------------- 1 | import time 2 | import csv 3 | from random import randint 4 | from selenium import webdriver 5 | from gen_names import gen_female_first_name, gen_last_name 6 | from gen_random_values import gen_number, gen_phone, gen_height 7 | 8 | page = webdriver.Firefox() 9 | page.maximize_window() 10 | time.sleep(0.5) 11 | page.get('http://localhost:8000/customer/add/') 12 | 13 | dict_ = gen_female_first_name() 14 | first_name = dict_['first_name'] 15 | last_name = gen_last_name() 16 | print(first_name, last_name) 17 | 18 | email = '{}.{}@example.com'.format(first_name[0].lower(), last_name.lower()) 19 | 20 | address_list = [] 21 | 22 | ''' Lendo os dados de address.csv ''' 23 | with open('fix/address.csv', 'r') as f: 24 | r = csv.DictReader(f) 25 | for dct in r: 26 | address_list.append(dct) 27 | f.close() 28 | 29 | INDEX = randint(0, 9) 30 | 31 | fields = [ 32 | ['id_first_name', first_name], 33 | ['id_last_name', last_name], 34 | ['id_email', email], 35 | ['id_phone', gen_phone()], 36 | ['id_address', address_list[INDEX]['address']], 37 | ['id_complement', address_list[INDEX]['complement']], 38 | ['id_district', address_list[INDEX]['district']], 39 | ['id_city', address_list[INDEX]['city']], 40 | ['id_uf', address_list[INDEX]['city']], 41 | ['id_cep', address_list[INDEX]['cep']], 42 | ['id_person_height', gen_height()], 43 | ['id_bust', gen_number(34, 42)], 44 | ['id_hip', gen_number(34, 42)], 45 | ['id_waist', gen_number(34, 44)], 46 | ['id_heel', gen_number(34, 44)], 47 | ['id_person_size', gen_number(34, 44)], 48 | ] 49 | 50 | for field in fields: 51 | search = page.find_element_by_id(field[0]) 52 | search.send_keys(field[1]) 53 | 54 | # button = page.find_element_by_id('id_submit') 55 | button = page.find_element_by_class_name('btn-primary') 56 | button.click() 57 | 58 | page.quit() 59 | -------------------------------------------------------------------------------- /dress/core/templates/core/customer_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}Clientes{% endblock title %} 4 | 5 | {% block content %} 6 | 7 | 20 | 21 | 24 | 25 |
26 | {% if customer_list %} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {% for customer in customer_list %} 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {% endfor %} 51 | 52 |
NomeEmailTelefoneCidadeUFAlturaTamanho
{{ customer.full_name }}{{ customer.email }}{{ customer.phone }}{{ customer.city }}{{ customer.get_uf_display }}{{ customer.person_height }}{{ customer.person_size }}
53 | {% else %} 54 |

Sem itens na lista.

55 | {% endif %} 56 |
57 | 58 |
59 |
60 |

Total: {{ page_obj.paginator.count }} cliente{{ page_obj.paginator.count|pluralize }}

61 |
62 | 63 | {% include "pagination.html" %} 64 | 65 | {% endblock content %} 66 | -------------------------------------------------------------------------------- /dress/core/graphics.py: -------------------------------------------------------------------------------- 1 | import json 2 | import itertools 3 | from django.db.models import Count 4 | from django.core.serializers.json import DjangoJSONEncoder 5 | from django.http import HttpResponse 6 | from .models import Customer, Dress, Order 7 | 8 | 9 | def customer_per_size_json(request): 10 | ''' JSON used to generate the graphic ''' 11 | ''' Quantidade de clientes por tamanho ''' 12 | data = Customer.objects.values('person_size')\ 13 | .annotate(quant=Count('person_size'))\ 14 | .order_by('person_size').values('person_size', 'quant') 15 | ''' 16 | Precisa reescrever a lista com os campos do gráfico, 17 | que são: 'tamanho' e 'quant'. 18 | ''' 19 | lista = [{'tamanho': i['person_size'], 'quant':i['quant']} for i in data] 20 | ''' 'quant':i['quant'] não era necessário, mas ... ''' 21 | resp = json.dumps(lista, cls=DjangoJSONEncoder) 22 | return HttpResponse(resp) 23 | 24 | 25 | def dress_per_color_json(request): 26 | ''' Quantidade de vestidos por cor ''' 27 | data = Dress.objects.values('color')\ 28 | .annotate(quant=Count('color'))\ 29 | .order_by('color').values('color', 'quant') 30 | ''' Reescrevendo a lista ''' 31 | lista = [{'cor': i['color'], 'quant':i['quant']} for i in data] 32 | resp = json.dumps(lista, cls=DjangoJSONEncoder) 33 | return HttpResponse(resp) 34 | 35 | 36 | def dress_per_size_json(request): 37 | ''' Quantidade de vestidos por tamanho ''' 38 | data = Dress.objects.values('dress_size')\ 39 | .annotate(quant=Count('dress_size'))\ 40 | .order_by('dress_size').values('dress_size', 'quant') 41 | ''' Reescrevendo a lista ''' 42 | lista = [{'tamanho': i['dress_size'], 'quant':i['quant']} for i in data] 43 | resp = json.dumps(lista, cls=DjangoJSONEncoder) 44 | return HttpResponse(resp) 45 | 46 | 47 | def order_per_day_json(request): 48 | ''' Quantidade de pedidos por dia ''' 49 | qs = Order.objects.values('created').values('created') 50 | grouped = itertools.groupby( 51 | qs, lambda d: d.get('created').strftime('%Y-%m-%d')) 52 | data = [{'dia': day, 'quant': len(list(this_day))} 53 | for day, this_day in grouped] 54 | resp = json.dumps(data, cls=DjangoJSONEncoder) 55 | return HttpResponse(resp) 56 | 57 | 58 | def order_value_json(request): 59 | ''' Valor de cada pedido ''' 60 | data = Order.objects.values('id', 'price') 61 | lista = [{'id': i['id'], 'price': float(i['price'])} for i in data] 62 | resp = json.dumps(lista, cls=DjangoJSONEncoder) 63 | return HttpResponse(resp) 64 | -------------------------------------------------------------------------------- /dress/core/templates/core/order_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load static %} 4 | 5 | {% block title %}Order Detail{% endblock title %} 6 | 7 | {% block content %} 8 | 9 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
Pedido{{ object.id }}
Data{{ object.created }}
Cliente{{ object.customer }}
Vestido{{ object.dress }}
Aluguel{{ object.price }}
42 | 43 | 44 | 47 | 48 | 49 | 52 |
53 | 54 | 55 | 75 | 76 | 77 | {% endblock content %} 78 | -------------------------------------------------------------------------------- /dress/core/templates/core/dress_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load static %} 4 | 5 | {% block title %}Dress Detail{% endblock title %} 6 | 7 | {% block content %} 8 | 9 | 14 | 15 |
16 |
17 | 18 |
19 |
20 |

{{ object.dress_model }}

21 |

{{ object.stylist }}

22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
Cor{{ object.get_color_display }}
Altura{{ object.dress_height }}
Tamanho{{ object.dress_size }}
43 | 44 | 45 | 48 | 49 | 50 | 53 |
54 | 55 | 56 | 76 | 77 | 78 | {% endblock content %} 79 | -------------------------------------------------------------------------------- /dress/core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.core.urlresolvers import reverse_lazy as r 3 | from django.views.generic import CreateView, ListView, DetailView 4 | from django.views.generic import UpdateView, DeleteView 5 | from rest_framework import viewsets 6 | from .mixins import NameSearchMixin, DressSearchMixin, OrderSearchMixin 7 | from .models import Customer, Dress, Order 8 | from .forms import CustomerForm, DressForm, OrderForm 9 | from .serializers import CustomerSerializer, DressSerializer, OrderSerializer 10 | 11 | 12 | def home(request): 13 | return render(request, 'index.html') 14 | 15 | 16 | class CustomerList(NameSearchMixin, ListView): 17 | model = Customer 18 | paginate_by = 5 19 | 20 | 21 | customer_detail = DetailView.as_view(model=Customer) 22 | 23 | customer_create = CreateView.as_view(model=Customer, form_class=CustomerForm) 24 | 25 | customer_update = UpdateView.as_view(model=Customer, form_class=CustomerForm) 26 | 27 | customer_delete = DeleteView.as_view( 28 | model=Customer, success_url=r('core:customer_list')) 29 | 30 | 31 | class DressList(DressSearchMixin, ListView): 32 | model = Dress 33 | paginate_by = 5 34 | 35 | 36 | dress_detail = DetailView.as_view(model=Dress) 37 | 38 | dress_create = CreateView.as_view(model=Dress, form_class=DressForm) 39 | 40 | dress_update = UpdateView.as_view(model=Dress, form_class=DressForm) 41 | 42 | dress_delete = DeleteView.as_view( 43 | model=Dress, success_url=r('core:dress_list')) 44 | 45 | 46 | class OrderList(OrderSearchMixin, ListView): 47 | model = Order 48 | paginate_by = 10 49 | 50 | 51 | order_detail = DetailView.as_view(model=Order) 52 | 53 | order_create = CreateView.as_view(model=Order, form_class=OrderForm) 54 | 55 | order_update = UpdateView.as_view(model=Order, form_class=OrderForm) 56 | 57 | order_delete = DeleteView.as_view( 58 | model=Order, success_url=r('core:order_list')) 59 | 60 | 61 | class CustomerViewSet(viewsets.ModelViewSet): 62 | ''' API endpoint that allows customers to be viewed or edited. ''' 63 | queryset = Customer.objects.all().order_by('first_name') 64 | serializer_class = CustomerSerializer 65 | authentication_classes = [] 66 | permission_classes = [] 67 | 68 | 69 | class DressViewSet(viewsets.ModelViewSet): 70 | ''' API endpoint that allows dress to be viewed or edited. ''' 71 | queryset = Dress.objects.all().order_by('dress_model') 72 | serializer_class = DressSerializer 73 | authentication_classes = [] 74 | permission_classes = [] 75 | 76 | 77 | class OrderViewSet(viewsets.ModelViewSet): 78 | ''' API endpoint that allows orders to be viewed or edited. ''' 79 | queryset = Order.objects.all().order_by('-created') 80 | serializer_class = OrderSerializer 81 | authentication_classes = [] 82 | permission_classes = [] 83 | -------------------------------------------------------------------------------- /dress/core/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load static %} 4 | 5 | {% block content %} 6 |
7 | 8 | 11 | 12 | 13 | 16 | 17 | Veja os 5 gráficos abaixo. 18 |
19 | 20 |
21 |
22 |
23 |
24 | Quantidade de clientes por tamanho 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | Quantidade de vestidos por tamanho 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 | Quantidade de vestidos por cor 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 |
60 |
61 |
62 |
63 | Quantidade de pedidos por dia 64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | 72 |
73 |
74 |
75 |
76 | Valor de cada pedido 77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | {% endblock content %} 85 | 86 | {% block js %} 87 | 88 | 89 | 90 | 91 | 92 | {% endblock js %} -------------------------------------------------------------------------------- /dress/core/static/css/social.css: -------------------------------------------------------------------------------- 1 | /* http://www.kodingmadesimple.com/2014/11/create-stylish-bootstrap-3-social-media-icons.html */ 2 | .social { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .social ul { 8 | margin: 0; 9 | padding: 5px; 10 | } 11 | 12 | .social ul li { 13 | margin: 5px; 14 | list-style: none outside none; 15 | display: inline-block; 16 | } 17 | 18 | .social i { 19 | width: 40px; 20 | height: 40px; 21 | color: #FFF; 22 | background-color: #909AA0; 23 | font-size: 22px; 24 | text-align:center; 25 | padding-top: 12px; 26 | border-radius: 50%; 27 | -moz-border-radius: 50%; 28 | -webkit-border-radius: 50%; 29 | -o-border-radius: 50%; 30 | transition: all ease 0.3s; 31 | -moz-transition: all ease 0.3s; 32 | -webkit-transition: all ease 0.3s; 33 | -o-transition: all ease 0.3s; 34 | -ms-transition: all ease 0.3s; 35 | text-decoration: none; 36 | } 37 | 38 | .social .fa-facebook { 39 | background: #4060A5; 40 | } 41 | 42 | .social .fa-twitter { 43 | background: #00ABE3; 44 | } 45 | 46 | .social .fa-google-plus { 47 | background: #e64522; 48 | } 49 | 50 | .social .fa-github { 51 | background: #343434; 52 | } 53 | 54 | .social .fa-pinterest { 55 | background: #cb2027; 56 | } 57 | 58 | .social .fa-linkedin { 59 | background: #0094BC; 60 | } 61 | 62 | .social .fa-flickr { 63 | background: #FF57AE; 64 | } 65 | 66 | .social .fa-instagram { 67 | background: #375989; 68 | } 69 | 70 | .social .fa-vimeo-square { 71 | background: #83DAEB; 72 | } 73 | 74 | .social .fa-stack-overflow { 75 | background: #FEA501; 76 | } 77 | 78 | .social .fa-dropbox { 79 | background: #017FE5; 80 | } 81 | 82 | .social .fa-tumblr { 83 | background: #3a5876; 84 | } 85 | 86 | .social .fa-dribbble { 87 | background: #F46899; 88 | } 89 | 90 | .social .fa-skype { 91 | background: #00C6FF; 92 | } 93 | 94 | .social .fa-stack-exchange { 95 | background: #4D86C9; 96 | } 97 | 98 | .social .fa-youtube { 99 | background: #FF1F25; 100 | } 101 | 102 | .social .fa-xing { 103 | background: #005C5E; 104 | } 105 | 106 | .social .fa-rss { 107 | background: #e88845; 108 | } 109 | 110 | .social .fa-foursquare { 111 | background: #09B9E0; 112 | } 113 | 114 | .social .fa-youtube-play { 115 | background: #DF192A; 116 | } 117 | 118 | .social .fa-slack { 119 | background: #4F3A4B; 120 | } 121 | 122 | .social .fa-whatsapp { 123 | background: #65BC54; 124 | } 125 | 126 | .socialfooter { 127 | margin: 0; 128 | padding: 0; 129 | } 130 | 131 | .socialfooter ul { 132 | margin: 0; 133 | padding: 5px; 134 | } 135 | 136 | .socialfooter ul li { 137 | margin: 5px; 138 | list-style: none outside none; 139 | display: inline-block; 140 | } 141 | 142 | .socialfooter i { 143 | color: #FFF; 144 | font-size: 22px; 145 | text-align:center; 146 | padding-top: 12px; 147 | border-radius: 50%; 148 | -moz-border-radius: 50%; 149 | -webkit-border-radius: 50%; 150 | -o-border-radius: 50%; 151 | transition: all ease 0.3s; 152 | -moz-transition: all ease 0.3s; 153 | -webkit-transition: all ease 0.3s; 154 | -o-transition: all ease 0.3s; 155 | -ms-transition: all ease 0.3s; 156 | text-decoration: none; 157 | } 158 | 159 | .socialfooter i:hover { 160 | color: #00ABE3; 161 | } 162 | -------------------------------------------------------------------------------- /dress/core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.shortcuts import resolve_url as r 3 | from localflavor.br.br_states import STATE_CHOICES 4 | from dress.utils.lists import COLORS 5 | 6 | 7 | class TimeStampedModel(models.Model): 8 | created = models.DateTimeField( 9 | 'criado em', auto_now_add=True, auto_now=False) 10 | modified = models.DateTimeField( 11 | 'modificado em', auto_now_add=False, auto_now=True) 12 | 13 | class Meta: 14 | abstract = True 15 | 16 | 17 | class Address(models.Model): 18 | address = models.CharField( 19 | u'endereço', max_length=100, null=True, blank=True) 20 | complement = models.CharField( 21 | 'complemento', max_length=100, null=True, blank=True) 22 | district = models.CharField( 23 | 'bairro', max_length=100, null=True, blank=True) 24 | city = models.CharField('cidade', max_length=100, null=True, blank=True) 25 | uf = models.CharField( 26 | 'UF', max_length=2, choices=STATE_CHOICES, null=True, blank=True) 27 | cep = models.CharField('CEP', max_length=9, null=True, blank=True) 28 | 29 | class Meta: 30 | abstract = True 31 | 32 | 33 | class Customer(TimeStampedModel, Address): 34 | first_name = models.CharField('nome', max_length=50) 35 | last_name = models.CharField( 36 | 'sobrenome', max_length=50, null=True, blank=True) 37 | email = models.EmailField(null=True, blank=True) 38 | phone = models.CharField('telefone', max_length=20) 39 | person_height = models.DecimalField( 40 | 'altura', max_digits=3, decimal_places=2) 41 | bust = models.PositiveIntegerField('busto') 42 | hip = models.PositiveIntegerField('quadril') 43 | waist = models.PositiveIntegerField('cintura') 44 | heel = models.PositiveIntegerField('salto') 45 | person_size = models.PositiveIntegerField('tamanho') 46 | 47 | class Meta: 48 | ordering = ['first_name'] 49 | verbose_name = 'cliente' 50 | verbose_name_plural = 'clientes' 51 | 52 | def __str__(self): 53 | return ' '.join(filter(None, [self.first_name, self.last_name])) 54 | 55 | full_name = property(__str__) 56 | 57 | def get_absolute_url(self): 58 | return r('core:customer_detail', pk=self.pk) 59 | 60 | 61 | class Dress(TimeStampedModel): 62 | dress_model = models.CharField('modelo', max_length=50) 63 | stylist = models.CharField('estilista', max_length=50) 64 | color = models.CharField('cor', max_length=20, choices=COLORS) 65 | dress_height = models.DecimalField( 66 | 'altura', max_digits=3, decimal_places=2) 67 | dress_size = models.PositiveIntegerField('tamanho') 68 | 69 | class Meta: 70 | ordering = ['dress_model'] 71 | verbose_name = 'vestido' 72 | verbose_name_plural = 'vestidos' 73 | 74 | def __str__(self): 75 | return self.dress_model 76 | 77 | def get_absolute_url(self): 78 | return r('core:dress_detail', pk=self.pk) 79 | 80 | 81 | class Order(TimeStampedModel): 82 | customer = models.ForeignKey('Customer', verbose_name='cliente') 83 | dress = models.ForeignKey('Dress', verbose_name='vestido') 84 | price = models.DecimalField('Aluguel', max_digits=6, decimal_places=2) 85 | 86 | class Meta: 87 | ordering = ['-created'] 88 | verbose_name = 'pedido' 89 | verbose_name_plural = 'pedidos' 90 | 91 | def __str__(self): 92 | return str(self.id) 93 | 94 | def get_absolute_url(self): 95 | return r('core:order_detail', pk=self.pk) 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dg-challenge 2 | 3 | 4 | ## Objetivo 5 | 6 | Nós somos a primeira plataforma online para aluguel de vestidos de grandes estilistas do país. 7 | Nossos principais produtos são vestidos e acessórios femininos. 8 | 9 | Considere que os nossos vestidos têm como principais características seu modelo, estilista, cor, altura e tamanho. 10 | 11 | Quando uma cliente faz uma locação precisamos saber seu nome, endereço, cidade, estado, cep, telefone celular, altura, busto, quadril, cintura, salto e tamanho que ela usa. 12 | 13 | Baseado nessas informações, seu desafio é estruturar: 14 | 15 | * A base de dados 16 | * A interface gráfica para administrar esses dados 17 | * Criar uma API REST para fazermos o CRUD de vestidos e clientes 18 | * Devemos poder atribuir vestidos a clientes tanto pela interface quanto pela API 19 | 20 | Plus: 21 | 22 | * Gostamos de gráficos 23 | * Gostamos de coisas customizadas, fora do default 24 | * Gostamos de Python e Django 25 | 26 | 27 | ## Versão 28 | 29 | * Python 3.5 30 | * Django==1.9.5 31 | 32 | 33 | ## Instalação 34 | 35 | * Clone o repositório. 36 | * Crie um virtualenv com Python 3.5 37 | * Ative o virtualenv. 38 | * Instale as dependências. 39 | * Configure a instância com o .env 40 | * Rode a migração 41 | * Crie um usuário username='admin' pass='demodemo' 42 | 43 | ``` 44 | git clone https://github.com/rg3915/dg-challenge.git 45 | cd dg-challenge 46 | python -m venv .venv 47 | source .venv/bin/activate 48 | PS1="(`basename \"$VIRTUAL_ENV\"`):/\W$ " # opcional 49 | pip install -r requirements.txt 50 | cp contrib/env-sample .env 51 | ./manage.py makemigrations core 52 | ./manage.py migrate 53 | ./manage.py createsuperuser --username='admin' --email='' 54 | ``` 55 | 56 | ## Testes 57 | 58 | ``` 59 | ./manage.py test 60 | ``` 61 | 62 | ## Shell do Django 63 | 64 | Execute os comandos shell a seguir. Eles inserem uma quantidade aleatória de registros variando entre 1 e 20. 65 | 66 | ``` 67 | make shell_customer 68 | make shell_dress 69 | make shell_order 70 | ``` 71 | 72 | ## Selenium 73 | 74 | Você também pode usar o **Selenium** para preencher os formulários. Para isso você vai precisar de **duas abas do terminal**. 75 | 76 | * Em uma você roda a app na porta 8000 77 | * E na outra você roda os comandos a seguir: 78 | 79 | ``` 80 | make selenium_customer 81 | make selenium_dress 82 | ``` 83 | 84 | O **pedido** é feito manualmente a partir de [http://localhost:8000/order/add/][0] . 85 | 86 | 87 | 88 | ## Api Rest 89 | 90 | A partir do post [Django Rest Framework Quickstart][1] e [Django Rest Framework Serialization][2] eu fiz a Api Rest. 91 | 92 | Neste exemplo também você vai precisar de **duas abas do terminal**. 93 | 94 | * Em uma você roda a app na porta 8000 95 | * E na outra você roda os comandos a seguir (usando [httpie][3]): 96 | 97 | **Obs:** para os exemplos a seguir considere que já exista um cliente e um vestido cadastrados. Os exemplos referem-se apenas aos **pedidos**. 98 | 99 | ``` 100 | http http://127.0.0.1:8000/api/customers/ 101 | http http://127.0.0.1:8000/api/customers/1/ 102 | http http://127.0.0.1:8000/api/dresses/1/ 103 | http http://127.0.0.1:8000/api/orders/1/ 104 | 105 | # Create 106 | http POST http://127.0.0.1:8000/api/orders/ customer=1 dress=1 price=450.99 107 | 108 | # Update 109 | http PUT http://127.0.0.1:8000/api/orders/1/ customer=1 dress=2 price=999.99 110 | 111 | # Delete 112 | http DELETE http://127.0.0.1:8000/api/orders/1/ 113 | ``` 114 | 115 | Também podemos usar o [cURL][4]. 116 | 117 | ``` 118 | curl http://127.0.0.1:8000/api/orders/1/ 119 | 120 | # Create 121 | curl -X POST http://127.0.0.1:8000/api/orders/ -H 'Content-Type: application/json' -d '{"customer": 1, "dress": 1, "price": "900.05"}' 122 | 123 | # Update 124 | curl -X PUT http://127.0.0.1:8000/api/orders/1/ -H 'Content-Type: application/json' -d '{"customer": 1, "dress": 2, "price": "854.92"}' 125 | 126 | # Delete 127 | curl -X DELETE http://127.0.0.1:8000/api/orders/1/ 128 | ``` 129 | 130 | 131 | 132 | ## Screenshot 133 | 134 | ![img](img/graphics.png) 135 | 136 | *Dress and Go* 137 | 138 | [0]: http://localhost:8000/order/add/ 139 | [1]: http://pythonclub.com.br/django-rest-framework-quickstart.html 140 | [2]: http://pythonclub.com.br/django-rest-framework-serialization.html 141 | [3]: https://github.com/jkbrzt/httpie#installation 142 | [4]: http://www.diego-garcia.info/2014/12/13/use-o-curl/ 143 | -------------------------------------------------------------------------------- /dress/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for dress project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.9.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.9/ref/settings/ 11 | """ 12 | 13 | import os 14 | from decouple import config, Csv 15 | from dj_database_url import parse as dburl 16 | 17 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 18 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 19 | 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | SECRET_KEY = config('SECRET_KEY') 26 | 27 | # SECURITY WARNING: don't run with debug turned on in production! 28 | DEBUG = config('DEBUG', default=False, cast=bool) 29 | 30 | ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv()) 31 | 32 | 33 | # Application definition 34 | 35 | INSTALLED_APPS = [ 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | # thirty apps 43 | 'bootstrapform', 44 | 'widget_tweaks', 45 | 'daterange_filter', 46 | 'django_extensions', 47 | 'rest_framework', 48 | # my apps 49 | 'dress.core', 50 | ] 51 | 52 | REST_FRAMEWORK = { 53 | 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',), 54 | 'PAGE_SIZE': 10 55 | } 56 | 57 | MIDDLEWARE_CLASSES = [ 58 | 'django.middleware.security.SecurityMiddleware', 59 | 'django.contrib.sessions.middleware.SessionMiddleware', 60 | 'django.middleware.common.CommonMiddleware', 61 | 'django.middleware.csrf.CsrfViewMiddleware', 62 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 63 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 64 | 'django.contrib.messages.middleware.MessageMiddleware', 65 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 66 | ] 67 | 68 | ROOT_URLCONF = 'dress.urls' 69 | 70 | TEMPLATES = [ 71 | { 72 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 73 | 'DIRS': [], 74 | 'APP_DIRS': True, 75 | 'OPTIONS': { 76 | 'context_processors': [ 77 | 'django.template.context_processors.debug', 78 | 'django.template.context_processors.request', 79 | 'django.contrib.auth.context_processors.auth', 80 | 'django.contrib.messages.context_processors.messages', 81 | ], 82 | }, 83 | }, 84 | ] 85 | 86 | WSGI_APPLICATION = 'dress.wsgi.application' 87 | 88 | default_dburl = 'sqlite:///' + os.path.join(BASE_DIR, 'db.sqlite3') 89 | DATABASES = { 90 | 'default': config('DATABASE_URL', default=default_dburl, cast=dburl), 91 | } 92 | 93 | # Database 94 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases 95 | 96 | 97 | # Password validation 98 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators 99 | 100 | AUTH_PASSWORD_VALIDATORS = [ 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 109 | }, 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 112 | }, 113 | ] 114 | 115 | 116 | # Internationalization 117 | # https://docs.djangoproject.com/en/1.9/topics/i18n/ 118 | 119 | LANGUAGE_CODE = 'pt-br' 120 | 121 | TIME_ZONE = 'America/Sao_Paulo' 122 | 123 | USE_I18N = True 124 | 125 | USE_L10N = True 126 | 127 | USE_TZ = True 128 | 129 | USE_THOUSAND_SEPARATOR = True 130 | 131 | DECIMAL_SEPARATOR = ',' 132 | 133 | 134 | # Static files (CSS, JavaScript, Images) 135 | # https://docs.djangoproject.com/en/1.9/howto/static-files/ 136 | 137 | STATIC_URL = '/static/' 138 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 139 | 140 | LOGIN_URL = '/admin/login/' 141 | -------------------------------------------------------------------------------- /dress/core/templates/core/customer_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load static %} 4 | 5 | {% block title %}Customer Detail{% endblock title %} 6 | 7 | {% block content %} 8 | 9 | 14 | 15 |
16 |
17 | 18 |
19 |
20 |

{{ object.full_name }}

21 | {% if object.email %} 22 |

{{ object.email }}

23 | {% endif %} 24 | 32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% if object.address %} 44 | 45 | 46 | 50 | 51 | 52 | 53 | 58 | 59 | {% endif %} 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
{{ object.phone }}
{{ object.address }} 47 | {% if object.complement %} - {{ object.complement }}{% endif %} 48 | {% if object.district %} - {{ object.district }}{% endif %} 49 |
54 | {% if object.city %}{{ object.city }}{% endif %} 55 | {% if object.uf %} - {{ object.uf }}{% endif %} 56 | {% if object.cep %} - {{ object.cep }}{% endif %} 57 |
Altura{{ object.person_height }}
Busto{{ object.bust }}
Quadril{{ object.hip }}
Cintura{{ object.waist }}
Salto{{ object.heel }}
Tamanho{{ object.person_size }}
88 | 89 | 90 | 93 | 94 | 95 | 98 |
99 | 100 | 101 | 121 | 122 | 123 | {% endblock content %} 124 | --------------------------------------------------------------------------------