├── 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 |
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 |
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 |
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 |
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 | Cliente |
32 | Vestido |
33 | Aluguel |
34 |
35 |
36 |
37 | {% for order in order_list %}
38 |
39 | | {{ order.id }} |
40 | {{ order.customer }} |
41 | {{ order.dress }} |
42 | R$ {{ order.price }} |
43 |
44 | {% endfor %}
45 |
46 |
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 | | Modelo |
31 | Estilista |
32 | Cor |
33 | Altura |
34 | Tamanho |
35 |
36 |
37 |
38 | {% for dress in dress_list %}
39 |
40 | | {{ dress.dress_model }} |
41 | {{ dress.stylist }} |
42 | {{ dress.color }} |
43 | {{ dress.dress_height }} |
44 | {{ dress.dress_size }} |
45 |
46 | {% endfor %}
47 |
48 |
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 | | Nome |
31 | Email |
32 | Telefone |
33 | Cidade |
34 | UF |
35 | Altura |
36 | Tamanho |
37 |
38 |
39 |
40 | {% for customer in customer_list %}
41 |
42 | | {{ customer.full_name }} |
43 | {{ customer.email }} |
44 | {{ customer.phone }} |
45 | {{ customer.city }} |
46 | {{ customer.get_uf_display }} |
47 | {{ customer.person_height }} |
48 | {{ customer.person_size }} |
49 |
50 | {% endfor %}
51 |
52 |
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 |
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 |
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 |
19 |
20 |
21 |
22 |
23 |
24 | Quantidade de clientes por tamanho
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Quantidade de vestidos por tamanho
38 |
39 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Quantidade de vestidos por cor
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Quantidade de pedidos por dia
64 |
65 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Valor de cada pedido
77 |
78 |
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 | 
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 |
23 | {% endif %}
24 |
32 |
33 |
34 |
35 |
36 |
88 |
89 |
90 |
93 |
94 |
95 |
98 |
99 |
100 |
101 |
121 |
122 |
123 | {% endblock content %}
124 |
--------------------------------------------------------------------------------