├── .gitignore ├── booking_service ├── application │ ├── booking │ │ ├── booking_dto.py │ │ ├── booking_manager.py │ │ └── booking_storage.py │ └── customers │ │ └── customer_dto.py ├── domain │ ├── booking │ │ ├── entities.py │ │ ├── enums.py │ │ ├── exceptions.py │ │ └── value_objects.py │ ├── customers │ │ ├── entities.py │ │ └── exceptions.py │ └── rooms │ │ └── entities.py └── tests │ ├── tests_booking_aggregate.py │ └── tests_booking_manager.py ├── clean.png └── infrastructure ├── api ├── __init__.py ├── api.http ├── api.py ├── models.py └── repositories.py ├── requirements.txt └── website ├── booking ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_booking_status.py │ └── __init__.py ├── models.py ├── repositories.py ├── templates │ ├── confirmation.html │ ├── delete_confirmation.html │ ├── index.html │ └── update.html ├── tests.py ├── urls.py └── views.py ├── manage.py └── website ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | *.pyc 3 | *.sqlite3 4 | *.db 5 | .idea 6 | -------------------------------------------------------------------------------- /booking_service/application/booking/booking_dto.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from booking_service.domain.booking.entities import Booking 3 | from booking_service.application.customers.customer_dto import CustomerDto 4 | from booking_service.domain.booking.enums import BookingStatuses 5 | 6 | class BookingDto(object): 7 | id: int 8 | checkin: datetime 9 | checkout: datetime 10 | customer: CustomerDto 11 | status: str 12 | 13 | def __init__(self, checkin: datetime, checkout: datetime, customer: CustomerDto): 14 | self.checkin = checkin 15 | self.checkout = checkout 16 | self.customer = customer 17 | self.id = None 18 | self.status = BookingStatuses.OPEN.name 19 | 20 | def to_domain(self): 21 | booking = Booking(self.checkin, self.checkout, self.customer.to_domain()) 22 | booking.id = self.id 23 | booking.status = BookingStatuses[self.status] 24 | return booking 25 | 26 | def to_dto(self, booking: Booking): 27 | customer_dto = self.customer.to_dto(booking.customer) 28 | booking_dto = BookingDto( 29 | checkin=booking.checkin, 30 | checkout=booking.checkout, 31 | customer=customer_dto) 32 | booking_dto.status = booking.status.name 33 | booking_dto.id = booking.id 34 | return booking_dto 35 | 36 | class UserDto(object): 37 | id: int 38 | name: str 39 | is_admin: bool 40 | 41 | def __init__(self, name: str, is_admin: bool) -> None: 42 | self.name = name 43 | self.is_admin = is_admin 44 | 45 | -------------------------------------------------------------------------------- /booking_service/application/booking/booking_manager.py: -------------------------------------------------------------------------------- 1 | from booking_service.domain.booking.exceptions import * 2 | from booking_service.domain.customers.exceptions import * 3 | from .booking_dto import BookingDto, UserDto 4 | from booking_service.domain.booking.enums import * 5 | from .booking_storage import BookingStorage 6 | 7 | class BookingManager(object): 8 | storage: BookingStorage 9 | 10 | def __init__(self, storage: BookingStorage) -> None: 11 | self.storage = storage 12 | 13 | def get_bookings(self, user_dto: UserDto): 14 | if user_dto.is_admin: 15 | return self.storage.get_all_bookings() 16 | else: 17 | return self.storage.get_filtered_bookings() 18 | 19 | def get_booking_by_id(self, booking_id: int, user_dto: UserDto): 20 | booking = self.storage.get_booking_by_id(booking_id) 21 | if booking.status == BookingStatuses.CANCELED.name and not user_dto.is_admin: 22 | return {'message': ErrorCodes.USERNOTALLOWEDTOACCESSDATA.value, 'code': ErrorCodes.USERNOTALLOWEDTOACCESSDATA.name} 23 | else: 24 | return {'message': SuccessCodes.SUCCESS.value, 'data': booking, 'code': SuccessCodes.SUCCESS.name} 25 | 26 | def create_new_booking(self, booking_dto: BookingDto): 27 | booking_aggregate = booking_dto.to_domain() 28 | 29 | try: 30 | booking_aggregate.create_booking() 31 | final_dto = booking_dto.to_dto(booking_aggregate) 32 | self.storage.save_booking(final_dto) 33 | return {'message': SuccessCodes.SUCCESS.value, 'code': SuccessCodes.SUCCESS.name} 34 | except CheckinDateCannotBeAfterCheckoutDate as e: 35 | return {'message': ErrorCodes.CHECKINAFTERCHECKOUT.value, 'code': ErrorCodes.CHECKINAFTERCHECKOUT.name} 36 | except CustomerCannotBeBlank as e: 37 | return {'message': ErrorCodes.CUSTOMERISREQUIRED.value, 'code': ErrorCodes.CUSTOMERISREQUIRED.name} 38 | except CustomerShouldBeOlderThan18 as e: 39 | return {'message': ErrorCodes.CUSTOMERSHOULDBEOLDERTHAN18.value, 'code': ErrorCodes.CUSTOMERSHOULDBEOLDERTHAN18.name} 40 | except InvalidCustomerDocumentException as e: 41 | return {'message': ErrorCodes.INVALIDCUSTOMERDOCUMENT.value, 'code': ErrorCodes.INVALIDCUSTOMERDOCUMENT.name} 42 | except Exception as e: 43 | return {'message': ErrorCodes.UNDEFINED.value, 'code': ErrorCodes.UNDEFINED.name} 44 | 45 | def update_booking(self, booking_dto: BookingDto): 46 | booking_aggregate = booking_dto.to_domain() 47 | try: 48 | booking_aggregate.update_booking() 49 | final_dto = booking_dto.to_dto(booking_aggregate) 50 | self.storage.update_booking(final_dto) 51 | return {'message': SuccessCodes.SUCCESS.value, 'code': SuccessCodes.SUCCESS.name} 52 | except CheckinDateCannotBeAfterCheckoutDate as e: 53 | return {'message': ErrorCodes.CHECKINAFTERCHECKOUT.value, 'code': ErrorCodes.CHECKINAFTERCHECKOUT.name} 54 | except CustomerCannotBeBlank as e: 55 | return {'message': ErrorCodes.CUSTOMERISREQUIRED.value, 'code': ErrorCodes.CUSTOMERISREQUIRED.name} 56 | except CustomerShouldBeOlderThan18 as e: 57 | return {'message': ErrorCodes.CUSTOMERSHOULDBEOLDERTHAN18.value, 'code': ErrorCodes.CUSTOMERSHOULDBEOLDERTHAN18.name} 58 | except InvalidCustomerDocumentException as e: 59 | return {'message': ErrorCodes.INVALIDCUSTOMERDOCUMENT.value, 'code': ErrorCodes.INVALIDCUSTOMERDOCUMENT.name} 60 | except BookingUpdateRequiresExistingBookingId as e: 61 | return {'message': ErrorCodes.UPDATEBOOKINGREQUIRESBOOKINGID.value, 'code': ErrorCodes.UPDATEBOOKINGREQUIRESBOOKINGID.name} 62 | except Exception as e: 63 | return {'message': ErrorCodes.UNDEFINED.value, 'code': ErrorCodes.UNDEFINED.name} 64 | 65 | def delete_booking(self, booking_id: int): 66 | try: 67 | booking = self.storage.get_booking_by_id(booking_id) 68 | domain_aggregate = booking.to_domain() 69 | domain_aggregate.delete_booking() 70 | final_dto = booking.to_dto(domain_aggregate) 71 | self.storage.delete_booking(final_dto) 72 | return {'message': SuccessCodes.SUCCESS.value, 'code': SuccessCodes.SUCCESS.name} 73 | except BookingWithThisStatusCannotBeDeleted as e: 74 | return {'message': ErrorCodes.BOOKINGSTATUSDOESNOTALLOWDELETE.value, 'code': ErrorCodes.BOOKINGSTATUSDOESNOTALLOWDELETE.name} 75 | except CheckinDateCannotBeAfterCheckoutDate as e: 76 | return {'message': ErrorCodes.CHECKINAFTERCHECKOUT.value, 'code': ErrorCodes.CHECKINAFTERCHECKOUT.name} 77 | except CustomerCannotBeBlank as e: 78 | return {'message': ErrorCodes.CUSTOMERISREQUIRED.value, 'code': ErrorCodes.CUSTOMERISREQUIRED.name} 79 | except CustomerShouldBeOlderThan18 as e: 80 | return {'message': ErrorCodes.CUSTOMERSHOULDBEOLDERTHAN18.value, 'code': ErrorCodes.CUSTOMERSHOULDBEOLDERTHAN18.name} 81 | except InvalidCustomerDocumentException as e: 82 | return {'message': ErrorCodes.INVALIDCUSTOMERDOCUMENT.value, 'code': ErrorCodes.INVALIDCUSTOMERDOCUMENT.name} 83 | except Exception as e: 84 | return {'message': ErrorCodes.UNDEFINED.value, 'code': ErrorCodes.UNDEFINED.name} 85 | -------------------------------------------------------------------------------- /booking_service/application/booking/booking_storage.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractclassmethod 2 | from .booking_dto import BookingDto 3 | 4 | class BookingStorage(ABC): 5 | 6 | @abstractclassmethod 7 | def save_booking(self, bookingDto: BookingDto): 8 | pass 9 | 10 | @abstractclassmethod 11 | def get_all_bookings(self): 12 | pass 13 | 14 | @abstractclassmethod 15 | def get_filtered_bookings(self): 16 | pass 17 | 18 | @abstractclassmethod 19 | def get_booking_by_id(self, id) -> BookingDto: 20 | pass 21 | 22 | @abstractclassmethod 23 | def update_booking(self, booking_dto: BookingDto): 24 | pass 25 | 26 | @abstractclassmethod 27 | def delete_booking(self, booking_dto: BookingDto): 28 | pass 29 | 30 | -------------------------------------------------------------------------------- /booking_service/application/customers/customer_dto.py: -------------------------------------------------------------------------------- 1 | from booking_service.domain.booking.entities import Customer 2 | 3 | class CustomerDto(object): 4 | name: str 5 | age: int 6 | document: str 7 | email: str 8 | id: int 9 | 10 | def __init__(self, name: str, age: int, document: str, email: str) -> None: 11 | self.name = name 12 | self.age = age 13 | self.document = document 14 | self.email = email 15 | self.id = None 16 | 17 | def to_domain(self): 18 | customer = Customer(self.name, self.age, self.document, self.email) 19 | return customer 20 | 21 | def to_dto(self, customer: Customer): 22 | return CustomerDto( 23 | name=customer.name, age=customer.age, document=customer.document, email=customer.email) -------------------------------------------------------------------------------- /booking_service/domain/booking/entities.py: -------------------------------------------------------------------------------- 1 | from .exceptions import * 2 | from booking_service.domain.customers.exceptions import * 3 | from datetime import datetime 4 | from booking_service.domain.customers.entities import Customer 5 | from booking_service.domain.rooms.entities import Room 6 | from .enums import BookingStatuses 7 | 8 | class Booking(object): 9 | checkin: datetime 10 | checkout: datetime 11 | customer: Customer 12 | status: BookingStatuses 13 | margin: float 14 | room: Room 15 | id: int 16 | 17 | def __init__(self, checkin: datetime, checkout: datetime, customer: Customer): 18 | self.checkin = checkin 19 | self.checkout = checkout 20 | self.customer = customer 21 | self.status = BookingStatuses.OPEN 22 | self.id = None 23 | 24 | def create_booking(self): 25 | self.is_valid() 26 | self.status = BookingStatuses.RESERVED 27 | 28 | def delete_booking(self): 29 | self.is_valid() 30 | 31 | if self.status == BookingStatuses.CANCELED: 32 | raise BookingWithThisStatusCannotBeDeleted('Booking is in a status that does not allow delte') 33 | 34 | self.status = BookingStatuses.DELETED 35 | 36 | def update_booking(self): 37 | self.is_valid() 38 | 39 | if not self.id: 40 | raise BookingUpdateRequiresExistingBookingId('Cannot update a record without its Id') 41 | 42 | def is_valid(self): 43 | if self.checkin > self.checkout: 44 | raise CheckinDateCannotBeAfterCheckoutDate("Checkin cannot be after Checkout") 45 | elif not self.customer: 46 | raise CustomerCannotBeBlank("Customer is a required information") 47 | 48 | self.customer.is_valid() 49 | 50 | return True 51 | -------------------------------------------------------------------------------- /booking_service/domain/booking/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class ErrorCodes(Enum): 4 | CHECKINAFTERCHECKOUT = 'Checkin date cannot be after checkout' 5 | CUSTOMERISREQUIRED = "Customer is required" 6 | CUSTOMERSHOULDBEOLDERTHAN18 = 'Customer should be older than 18' 7 | INVALIDCUSTOMERDOCUMENT = 'Invalid customer document' 8 | USERNOTALLOWEDTOACCESSDATA = 'User not allowed to access this data' 9 | UPDATEBOOKINGREQUIRESBOOKINGID = 'Cannot update a booking without its id' 10 | BOOKINGSTATUSDOESNOTALLOWDELETE = 'Booking with this status does not allow delete' 11 | UNDEFINED = 'Undefined' 12 | 13 | class SuccessCodes(Enum): 14 | SUCCESS = 'Success' 15 | 16 | class BookingStatuses(Enum): 17 | OPEN = 0 18 | RESERVED = 1 19 | FINISHED = 2 20 | CANCELED = 3 21 | DELETED = 4 22 | -------------------------------------------------------------------------------- /booking_service/domain/booking/exceptions.py: -------------------------------------------------------------------------------- 1 | class CheckinDateCannotBeAfterCheckoutDate(Exception): 2 | def __init__(self, message): 3 | self.message = message 4 | 5 | class CustomerCannotBeBlank(Exception): 6 | def __init__(self, message): 7 | self.message = message 8 | 9 | class BookingUpdateRequiresExistingBookingId(Exception): 10 | def __init__(self, message): 11 | self.message = message 12 | 13 | class BookingWithThisStatusCannotBeDeleted(Exception): 14 | def __init__(self, message): 15 | self.message = message 16 | -------------------------------------------------------------------------------- /booking_service/domain/booking/value_objects.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gpzim98/DjangoFlaskCleanArchitecture/7032c76837dc3336382e9e5c4968fc6d417aed44/booking_service/domain/booking/value_objects.py -------------------------------------------------------------------------------- /booking_service/domain/customers/entities.py: -------------------------------------------------------------------------------- 1 | from .exceptions import * 2 | 3 | class Customer(object): 4 | name: str 5 | age: int 6 | document: str 7 | email: str 8 | 9 | def __init__(self, name: str, age: int, document: str, email: str) -> None: 10 | self.name = name 11 | self.age = age 12 | self.document = document 13 | self.email = email 14 | 15 | def is_valid(self): 16 | if len(self.document) < 5: 17 | raise InvalidCustomerDocumentException('Invalid document number') 18 | elif self.age < 18: 19 | raise CustomerShouldBeOlderThan18('Customer should be older than 18') 20 | 21 | return True -------------------------------------------------------------------------------- /booking_service/domain/customers/exceptions.py: -------------------------------------------------------------------------------- 1 | class CustomerShouldBeOlderThan18(Exception): 2 | def __init__(self, message): 3 | self.message = message 4 | 5 | class InvalidCustomerDocumentException(Exception): 6 | def __init__(self, message): 7 | self.message = message 8 | -------------------------------------------------------------------------------- /booking_service/domain/rooms/entities.py: -------------------------------------------------------------------------------- 1 | class Room(object): 2 | room_number: int 3 | reserved: bool -------------------------------------------------------------------------------- /booking_service/tests/tests_booking_aggregate.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from datetime import datetime, timedelta 3 | import sys 4 | sys.path.append('..') 5 | sys.path.append('../..') 6 | from domain.booking.entities import Booking 7 | from domain.booking.exceptions import * 8 | from domain.customers.exceptions import * 9 | from domain.customers.entities import Customer 10 | 11 | class BookingAggregateTests(unittest.TestCase): 12 | 13 | def test_checkin_date_cannot_be_after_checkout_date(self): 14 | checkin = datetime.today() 15 | checkout = datetime.today() - timedelta(days=1) 16 | customer = Customer('MyCustomer', 18, '12563', 'a@a.com') 17 | booking = Booking(checkin=checkin, checkout=checkout, customer=customer) 18 | 19 | with self.assertRaises(CheckinDateCannotBeAfterCheckoutDate) as ex: 20 | booking.is_valid() 21 | 22 | exception = ex.exception 23 | self.assertEqual(exception.message, "Checkin cannot be after Checkout") 24 | 25 | def test_checkin_date_cannot_be_after_checkout_date2(self): 26 | checkin = datetime.utcnow() 27 | checkout = datetime.today() - timedelta(days=1) 28 | customer = Customer('MyCustomer', 18, '12453', 'a@a.com') 29 | booking = Booking(checkin=checkin, checkout=checkout, customer=customer) 30 | 31 | self.assertRaises(CheckinDateCannotBeAfterCheckoutDate, booking.is_valid) 32 | 33 | def test_customer_cannot_be_blank(self): 34 | checkin = datetime.utcnow() 35 | checkout = datetime.today() 36 | booking = Booking(checkin=checkin, checkout=checkout, customer=None) 37 | 38 | self.assertRaises(CustomerCannotBeBlank, booking.is_valid) 39 | 40 | def test_customer_must_have_valid_document(self): 41 | checkin = datetime.utcnow() 42 | checkout = datetime.today() 43 | customer = Customer('MyCustomer', 18, '123', 'a@a.com') 44 | booking = Booking(checkin=checkin, checkout=checkout, customer=customer) 45 | 46 | self.assertRaises(InvalidCustomerDocumentException, booking.is_valid) 47 | 48 | def test_customer_happy_path(self): 49 | checkin = datetime.utcnow() 50 | checkout = datetime.today() 51 | customer = Customer('MyCustomer', 18, '12356', 'a@a.com') 52 | booking = Booking(checkin=checkin, checkout=checkout, customer=customer) 53 | 54 | self.assertTrue(booking.is_valid()) 55 | 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | -------------------------------------------------------------------------------- /booking_service/tests/tests_booking_manager.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from datetime import datetime, timedelta 3 | import sys 4 | from booking_service.application.booking.booking_dto import UserDto 5 | from booking_service.domain.booking.enums import BookingStatuses 6 | sys.path.append('..') 7 | sys.path.append('../..') 8 | from domain.booking.exceptions import * 9 | from domain.customers.exceptions import * 10 | from application.booking.booking_manager import BookingManager 11 | from application.booking.booking_dto import BookingDto 12 | from application.customers.customer_dto import CustomerDto 13 | from application.booking.booking_storage import BookingStorage 14 | 15 | class DummyStorage(BookingStorage): 16 | def save_booking(self, bookingDto: BookingDto): 17 | return True 18 | 19 | def get_booking_by_id(self, id) -> BookingDto: 20 | if id == 1: 21 | checkin = datetime.today() 22 | checkout = datetime.today() 23 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 24 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 25 | booking_dto.id = 1 26 | booking_dto.status = BookingStatuses.CANCELED.name 27 | else: 28 | checkin = datetime.today() 29 | checkout = datetime.today() 30 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 31 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 32 | booking_dto.id = 2 33 | booking_dto.status = BookingStatuses.RESERVED 34 | return booking_dto 35 | 36 | def update_booking(self, booking_dto: BookingDto): 37 | pass 38 | 39 | def get_all_bookings(self): 40 | checkin = datetime.today() 41 | checkout = datetime.today() 42 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 43 | booking_dto1 = BookingDto(checkin, checkout, customer) 44 | 45 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 46 | booking_dto2 = BookingDto(checkin, checkout, customer) 47 | admin_bookings = [booking_dto1, booking_dto2] 48 | return admin_bookings 49 | 50 | def get_filtered_bookings(self): 51 | checkin = datetime.today() 52 | checkout = datetime.today() 53 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 54 | booking_dto1 = BookingDto(checkin, checkout, customer) 55 | 56 | filtered_bookings = [booking_dto1] 57 | 58 | return filtered_bookings 59 | 60 | def delete_booking(self, booking_dto: BookingDto): 61 | pass 62 | 63 | class BookingAggregateManagerTests(unittest.TestCase): 64 | def __init__(self, methodName: str = ...) -> None: 65 | self.dummy_storage = DummyStorage() 66 | super().__init__(methodName) 67 | 68 | def test_get_all_bookings_admin(self): 69 | manager = BookingManager(self.dummy_storage) 70 | user_dto = UserDto('admin', True) 71 | bookings = manager.get_bookings(user_dto) 72 | self.assertEqual(len(bookings), 2) 73 | 74 | def test_get_all_bookings_non_admin(self): 75 | manager = BookingManager(self.dummy_storage) 76 | user_dto = UserDto('non_admin', False) 77 | user_dto.id = 1 78 | bookings = manager.get_bookings(user_dto) 79 | self.assertEqual(len(bookings), 1) 80 | 81 | def test_checkin_date_cannot_be_after_checkout(self): 82 | checkin = datetime.today() 83 | checkout = datetime.today() - timedelta(days=1) 84 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 85 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 86 | manager = BookingManager(self.dummy_storage) 87 | res = manager.create_new_booking(booking_dto) 88 | self.assertEqual(res['code'], 'CHECKINAFTERCHECKOUT') 89 | 90 | def test_customer_should_be_older_than_18(self): 91 | checkin = datetime.today() 92 | checkout = datetime.today() 93 | customer = CustomerDto("Customer", 17, "doc123", "a@a.com") 94 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 95 | manager = BookingManager(self.dummy_storage) 96 | res = manager.create_new_booking(booking_dto) 97 | self.assertEqual(res['code'], 'CUSTOMERSHOULDBEOLDERTHAN18') 98 | 99 | def test_customer_document_should_be_valid(self): 100 | checkin = datetime.today() 101 | checkout = datetime.today() 102 | customer = CustomerDto("Customer", 18, "doc1", "a@a.com") 103 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 104 | manager = BookingManager(self.dummy_storage) 105 | res = manager.create_new_booking(booking_dto) 106 | self.assertEqual(res['code'], 'INVALIDCUSTOMERDOCUMENT') 107 | 108 | def test_create(self): 109 | checkin = datetime.utcnow() 110 | checkout = datetime.today() 111 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 112 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 113 | manager = BookingManager(self.dummy_storage) 114 | res = manager.create_new_booking(booking_dto) 115 | self.assertEqual(res['code'], 'SUCCESS') 116 | 117 | def test_get_booking_by_id_admin_should_see_canceled_bookings(self): 118 | user_dto = UserDto('admin', True) 119 | manager = BookingManager(self.dummy_storage) 120 | res = manager.get_booking_by_id(1, user_dto) 121 | self.assertEqual(res['code'], 'SUCCESS') 122 | 123 | def test_get_booking_by_id_non_admin_should_not_see_canceled_bookings(self): 124 | user_dto = UserDto('non_admin', False) 125 | manager = BookingManager(self.dummy_storage) 126 | res = manager.get_booking_by_id(1, user_dto) 127 | self.assertEqual(res['code'], 'USERNOTALLOWEDTOACCESSDATA') 128 | 129 | def test_get_booking_by_id_admin_should_see_not_canceled_bookings(self): 130 | user_dto = UserDto('admin', True) 131 | manager = BookingManager(self.dummy_storage) 132 | res = manager.get_booking_by_id(2, user_dto) 133 | self.assertEqual(res['code'], 'SUCCESS') 134 | 135 | def test_get_booking_by_id_non_admin_should_see_not_canceled_bookings(self): 136 | user_dto = UserDto('non_admin', False) 137 | manager = BookingManager(self.dummy_storage) 138 | res = manager.get_booking_by_id(2, user_dto) 139 | self.assertEqual(res['code'], 'SUCCESS') 140 | 141 | def test_update_booking_requires_booking_id(self): 142 | checkin = datetime.utcnow() 143 | checkout = datetime.today() 144 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 145 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 146 | manager = BookingManager(self.dummy_storage) 147 | res = manager.update_booking(booking_dto) 148 | self.assertEqual(res['code'], 'UPDATEBOOKINGREQUIRESBOOKINGID') 149 | 150 | def test_update_booking_should_fail_if_customer_is_not_older_than_18(self): 151 | checkin = datetime.utcnow() 152 | checkout = datetime.today() 153 | customer = CustomerDto("Customer", 17, "doc123", "a@a.com") 154 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 155 | booking_dto.id = 1 156 | manager = BookingManager(self.dummy_storage) 157 | res = manager.update_booking(booking_dto) 158 | self.assertEqual(res['code'], 'CUSTOMERSHOULDBEOLDERTHAN18') 159 | 160 | def test_update_booking_should_fail_if_customer_doc_is_invalid(self): 161 | checkin = datetime.utcnow() 162 | checkout = datetime.today() 163 | customer = CustomerDto("Customer", 18, "inv", "a@a.com") 164 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 165 | booking_dto.id = 1 166 | manager = BookingManager(self.dummy_storage) 167 | res = manager.update_booking(booking_dto) 168 | self.assertEqual(res['code'], 'INVALIDCUSTOMERDOCUMENT') 169 | 170 | def test_update_booking_happy_path(self): 171 | checkin = datetime.utcnow() 172 | checkout = datetime.today() 173 | customer = CustomerDto("Customer", 18, "doc123", "a@a.com") 174 | booking_dto = BookingDto(checkin=checkin, checkout=checkout, customer=customer) 175 | booking_dto.id = 1 176 | manager = BookingManager(self.dummy_storage) 177 | res = manager.update_booking(booking_dto) 178 | self.assertEqual(res['code'], 'SUCCESS') 179 | 180 | if __name__ == '__main__': 181 | unittest.main() 182 | -------------------------------------------------------------------------------- /clean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gpzim98/DjangoFlaskCleanArchitecture/7032c76837dc3336382e9e5c4968fc6d417aed44/clean.png -------------------------------------------------------------------------------- /infrastructure/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gpzim98/DjangoFlaskCleanArchitecture/7032c76837dc3336382e9e5c4968fc6d417aed44/infrastructure/api/__init__.py -------------------------------------------------------------------------------- /infrastructure/api/api.http: -------------------------------------------------------------------------------- 1 | # GET http://127.0.0.1:5000/bookings 2 | # Content-Type: application/json 3 | # Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoibm9uYWRtaW4iLCJpc19hZG1pbiI6ZmFsc2UsImV4cCI6MTY2OTU4NzAxMH0.qX1k8L0zsdCG2Moo6zrALJLNI5yeC2iSBV2LXuOMhls 4 | 5 | 6 | # POST http://127.0.0.1:5000/create-booking 7 | # Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpc19hZG1pbiI6dHJ1ZSwiZXhwIjoxNjY5NjgxNzUwfQ.fT7zKzZrCHHfuBe91qJYG4GVTsuWAnmAdhcxxkTc3ng 8 | # content-type: application/json 9 | 10 | # { 11 | # "name": "Fulano1", 12 | # "checkin": "2022-12-15T12:00", 13 | # "checkout": "2022-12-15T12:00", 14 | # "age": 18, 15 | # "document": "12333", 16 | # "email": "fulanodetal2@gmail.com" 17 | # } 18 | 19 | DELETE http://127.0.0.1:5000/delete-booking/5 20 | Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpc19hZG1pbiI6dHJ1ZSwiZXhwIjoxNjY5NzE5Mzc0fQ.d9Bwho_hZ4Ko-gyxnGv1KTre4jrMN7RVpP847295pDQ 21 | -------------------------------------------------------------------------------- /infrastructure/api/api.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, make_response, request, session 2 | import jwt 3 | from datetime import datetime, timedelta 4 | from .models import db 5 | from .repositories import BookingRepository, UserRepository, BookingManager, UserDto, BookingDto, CustomerDto 6 | from functools import wraps 7 | 8 | app = Flask(__name__) 9 | app.config['SECRET_KEY'] = '7SIDFYDS7FYSFI7BKJBIU4BF344JKF' 10 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hotel.db' 11 | db.init_app(app) 12 | 13 | app.app_context().push() 14 | # db.create_all() 15 | 16 | def token_required(f): 17 | @wraps(f) 18 | def decorated(*args, **kwargs): 19 | token = request.headers['Authorization'] 20 | if not token: 21 | return make_response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'}) 22 | try: 23 | data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"]) 24 | session['current_user'] = {'user': data['user'], 'is_admin': data['is_admin']} 25 | except: 26 | return make_response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'}) 27 | 28 | return f(*args, **kwargs) 29 | return decorated 30 | 31 | 32 | @app.route('/login') 33 | def login(): 34 | auth = request.authorization 35 | if auth: 36 | rep = UserRepository() 37 | user = rep.get_user(auth.username, auth.password) 38 | if user: 39 | data = {'user': auth.username, 'is_admin': user.is_admin, 'exp': datetime.utcnow() + timedelta(minutes=90)} 40 | token = jwt.encode(data, app.config['SECRET_KEY']) 41 | return {'token': token} 42 | return make_response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'}) 43 | 44 | 45 | @app.route("/") 46 | def hello_world(): 47 | return {'message': 'Hello world'} 48 | 49 | @app.route('/bookings') 50 | @token_required 51 | def bookings(): 52 | repository = BookingRepository() 53 | manager = BookingManager(repository) 54 | user_dto = UserDto(session['current_user']['user'], session['current_user']['is_admin']) 55 | 56 | bookings = manager.get_bookings(user_dto) 57 | return get_serializable_booking_list(bookings) 58 | 59 | 60 | @app.route('/create-booking', methods=['POST']) 61 | @token_required 62 | def create_booking(): 63 | data = request.get_json() 64 | checkin = datetime.strptime(data['checkin'], "%Y-%m-%dT%H:%M") 65 | checkout = datetime.strptime(data['checkout'],"%Y-%m-%dT%H:%M") 66 | 67 | customer_dto = get_customer_dto_from_request(data) 68 | 69 | booking_dto = BookingDto(checkin, checkout, customer_dto) 70 | repository = BookingRepository() 71 | manager = BookingManager(repository) 72 | resp = manager.create_new_booking(booking_dto) 73 | 74 | if resp['code'] == 'SUCCESS': 75 | return make_response(resp, 201) 76 | 77 | return make_response(resp, 400) 78 | 79 | @app.route('/delete-booking/', methods=['DELETE']) 80 | @token_required 81 | def delete_booking(id): 82 | repository = BookingRepository() 83 | manager = BookingManager(repository) 84 | resp = manager.delete_booking(id) 85 | 86 | if resp['code'] == 'SUCCESS': 87 | return make_response(resp, 201) 88 | 89 | return make_response(resp, 400) 90 | 91 | def get_customer_dto_from_request(data): 92 | name = data['name'] 93 | age = int(data['age']) 94 | document = data['document'] 95 | email = data['email'] 96 | return CustomerDto(name, age, document, email) 97 | 98 | def get_serializable_booking_list(bookings): 99 | booking_list = [] 100 | for booking in bookings: 101 | b = { 102 | 'id': booking.id, 103 | 'checkin': booking.checkin, 104 | 'checkout': booking.checkout, 105 | 'customer_id': booking.customer.id, 106 | 'customer_name': booking.customer.name, 107 | 'status': booking.status 108 | } 109 | booking_list.append(b) 110 | return booking_list -------------------------------------------------------------------------------- /infrastructure/api/models.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | 5 | class Customer(db.Model): 6 | id = db.Column(db.Integer, primary_key=True) 7 | age = db.Column(db.Integer) 8 | name = db.Column(db.String(80), unique=True, nullable=False) 9 | document = db.Column(db.String(120), unique=True, nullable=False) 10 | email = db.Column(db.String(120), unique=True, nullable=False) 11 | 12 | def __repr__(self): 13 | return '' % self.name 14 | 15 | class User(db.Model): 16 | id = db.Column(db.Integer, primary_key=True) 17 | name = db.Column(db.String(80), unique=True, nullable=False) 18 | username = db.Column(db.String(80), unique=True, nullable=False) 19 | password = db.Column(db.String(80)) 20 | is_admin = db.Column(db.Boolean()) 21 | 22 | 23 | class Booking(db.Model): 24 | id = db.Column(db.Integer, primary_key=True) 25 | status = db.Column(db.String(20), unique=False, nullable=False) 26 | checkin = db.Column(db.DateTime) 27 | checkout = db.Column(db.DateTime) 28 | customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'), nullable=False) 29 | -------------------------------------------------------------------------------- /infrastructure/api/repositories.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('..') 3 | sys.path.append('../..') 4 | from booking_service.application.booking.booking_storage import BookingStorage 5 | from booking_service.application.booking.booking_manager import BookingManager 6 | from booking_service.application.booking.booking_dto import CustomerDto, BookingDto, UserDto 7 | from .models import Booking, User, db, Customer 8 | 9 | class BookingRepository(BookingStorage): 10 | def __init__(self): 11 | self.db = db 12 | 13 | def save_booking(self, booking_dto: BookingDto): 14 | customer = booking_dto.customer 15 | new = Customer( 16 | name=customer.name, age=customer.age, document=customer.document, email=customer.email) 17 | db.session.add(new) 18 | db.session.commit() 19 | db.session.flush() 20 | 21 | booking = Booking(status=booking_dto.status, checkin=booking_dto.checkin, checkout=booking_dto.checkout, customer_id=new.id) 22 | db.session.add(booking) 23 | db.session.commit() 24 | 25 | def get_booking_by_id(self, id) -> BookingDto: 26 | booking = Booking.query.get(id) 27 | customer = Customer.query.get(booking.customer_id) 28 | return self._model_to_dto(booking, customer) 29 | 30 | def update_booking(self, booking_dto: BookingDto): 31 | pass 32 | 33 | def delete_booking(self, booking_dto: BookingDto): 34 | booking = Booking.query.get(booking_dto.id) 35 | booking.status = booking_dto.status 36 | db.session.commit() 37 | 38 | def _model_to_dto(self, booking: Booking, customer: Customer): 39 | customer_dto = CustomerDto(customer.name, customer.age, customer.document, customer.email) 40 | booking_dto = BookingDto(booking.checkin, booking.checkout, customer_dto) 41 | booking_dto.id = booking.id 42 | booking_dto.status = booking.status 43 | return booking_dto 44 | 45 | def get_all_bookings(self): 46 | bookings = Booking.query.all() 47 | bookings_dto = [] 48 | for booking in bookings: 49 | customer = Customer.query.filter_by(id=booking.customer_id).first() 50 | bookings_dto.append(self._model_to_dto(booking, customer)) 51 | return bookings_dto 52 | 53 | def get_filtered_bookings(self): 54 | bookings = Booking.query.filter(Booking.status.notin_(['CANCELED'])) 55 | bookings_dto = [] 56 | for booking in bookings: 57 | customer = Customer.query.filter_by(id=booking.customer_id).first() 58 | bookings_dto.append(self._model_to_dto(booking, customer)) 59 | return bookings_dto 60 | 61 | 62 | class UserRepository(object): 63 | def get_user(self, username, password): 64 | return User.query.filter_by(username=username, password=password).first() 65 | -------------------------------------------------------------------------------- /infrastructure/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.5.2 2 | click==8.1.3 3 | colorama==0.4.6 4 | Django==4.1.3 5 | Flask==2.2.2 6 | Flask-Login==0.6.2 7 | Flask-SQLAlchemy==3.0.2 8 | greenlet==2.0.1 9 | importlib-metadata==5.1.0 10 | itsdangerous==2.1.2 11 | Jinja2==3.1.2 12 | MarkupSafe==2.1.1 13 | PyJWT==2.6.0 14 | SQLAlchemy==1.4.44 15 | sqlparse==0.4.3 16 | tzdata==2022.6 17 | Werkzeug==2.2.2 18 | zipp==3.11.0 -------------------------------------------------------------------------------- /infrastructure/website/booking/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gpzim98/DjangoFlaskCleanArchitecture/7032c76837dc3336382e9e5c4968fc6d417aed44/infrastructure/website/booking/__init__.py -------------------------------------------------------------------------------- /infrastructure/website/booking/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Booking, Customer 3 | 4 | admin.site.register(Booking) 5 | admin.site.register(Customer) 6 | -------------------------------------------------------------------------------- /infrastructure/website/booking/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BookingConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'booking' 7 | -------------------------------------------------------------------------------- /infrastructure/website/booking/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.3 on 2022-11-20 10:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Customer', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=100)), 20 | ('document', models.CharField(max_length=100)), 21 | ('email', models.EmailField(max_length=100)), 22 | ('age', models.IntegerField()), 23 | ], 24 | ), 25 | migrations.CreateModel( 26 | name='Booking', 27 | fields=[ 28 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 29 | ('checkin', models.DateField()), 30 | ('checkout', models.DateField()), 31 | ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.customer')), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /infrastructure/website/booking/migrations/0002_booking_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.3 on 2022-11-20 13:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('booking', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='booking', 15 | name='status', 16 | field=models.CharField(choices=[('OPEN', 'Open'), ('RESERVED', 'Reserved'), ('FINISHED', 'Finishd'), ('CANCELED', 'Canceled')], default='OPEN', max_length=20), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /infrastructure/website/booking/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gpzim98/DjangoFlaskCleanArchitecture/7032c76837dc3336382e9e5c4968fc6d417aed44/infrastructure/website/booking/migrations/__init__.py -------------------------------------------------------------------------------- /infrastructure/website/booking/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | BOOKING_STATUSES = [ 4 | ('OPEN', 'Open'), 5 | ('RESERVED', 'Reserved'), 6 | ('FINISHED', 'Finishd'), 7 | ('CANCELED', 'Canceled'), 8 | ('DELETED', 'Deleted'), 9 | ] 10 | 11 | class Customer(models.Model): 12 | name = models.CharField(max_length=100, blank=False, null=False) 13 | document = models.CharField(max_length=100, blank=False, null=False) 14 | email = models.EmailField(max_length=100, blank=False, null=False) 15 | age = models.IntegerField() 16 | 17 | def __str__(self) -> str: 18 | return self.name 19 | 20 | class Booking(models.Model): 21 | checkin = models.DateField(auto_now=False) 22 | checkout = models.DateField(auto_now=False) 23 | customer = models.ForeignKey(Customer, on_delete=models.CASCADE) 24 | status = models.CharField( 25 | max_length=20, 26 | choices=BOOKING_STATUSES, 27 | default='OPEN', 28 | ) 29 | 30 | def __str__(self) -> str: 31 | return "Booking for: " + self.customer.name 32 | -------------------------------------------------------------------------------- /infrastructure/website/booking/repositories.py: -------------------------------------------------------------------------------- 1 | from booking_service.application.booking.booking_storage import BookingStorage 2 | from booking_service.application.booking.booking_dto import BookingDto 3 | from booking_service.application.customers.customer_dto import CustomerDto 4 | from booking_service.domain.booking.enums import BookingStatuses 5 | from .models import Customer, Booking 6 | from django.db import transaction 7 | 8 | 9 | class BookingRepository(BookingStorage): 10 | def _customer_dto_to_model(self, customerDto: CustomerDto): 11 | customer = Customer() 12 | customer.name = customerDto.name 13 | customer.age = customerDto.age 14 | customer.document = customerDto.document 15 | customer.email = customerDto.email 16 | customer.id = customerDto.id 17 | 18 | return customer 19 | 20 | def _booking_dto_to_model(self, booking_dto: BookingDto): 21 | booking = Booking() 22 | booking.checkin = booking_dto.checkin 23 | booking.checkout = booking_dto.checkout 24 | booking.status = booking_dto.status 25 | booking.id = booking_dto.id 26 | return booking 27 | 28 | def _model_to_dto(self, booking: Booking): 29 | customer_dto = CustomerDto( 30 | booking.customer.name, 31 | booking.customer.age, 32 | booking.customer.document, 33 | booking.customer.email) 34 | booking_dto = BookingDto(booking.checkin, booking.checkout, customer_dto) 35 | booking_dto.status = booking.status 36 | booking_dto.id = booking.id 37 | return booking_dto 38 | 39 | @transaction.atomic 40 | def save_booking(self, booking_dto: BookingDto): 41 | customer = self._customer_dto_to_model(booking_dto.customer) 42 | customer.save() 43 | booking = self._booking_dto_to_model(booking_dto) 44 | booking.customer = customer 45 | booking.save() 46 | 47 | def get_all_bookings(self): 48 | bookings = Booking.objects.all() 49 | bookings_dto = [] 50 | for booking in bookings: 51 | bookings_dto.append(self._model_to_dto(booking)) 52 | return bookings_dto 53 | 54 | def get_filtered_bookings(self): 55 | bookings = Booking.objects.exclude(status=BookingStatuses.CANCELED.name) 56 | bookings_dto = [] 57 | for booking in bookings: 58 | bookings_dto.append(self._model_to_dto(booking)) 59 | return bookings_dto 60 | 61 | def get_booking_by_id(self, id) -> BookingDto: 62 | booking = Booking.objects.get(id=id) 63 | return self._model_to_dto(booking) 64 | 65 | @transaction.atomic 66 | def update_booking(self, booking_dto: BookingDto): 67 | booking = Booking.objects.get(id=booking_dto.id) 68 | booking.checkin = booking_dto.checkin 69 | booking.checkout = booking_dto.checkout 70 | booking.customer.name = booking_dto.customer.name 71 | booking.customer.email = booking_dto.customer.email 72 | booking.customer.document = booking_dto.customer.document 73 | booking.customer.save() 74 | booking.save() 75 | 76 | def delete_booking(self, booking_dto: BookingDto): 77 | booking = Booking.objects.get(id=booking_dto.id) 78 | booking.status = booking_dto.status 79 | booking.save() 80 | -------------------------------------------------------------------------------- /infrastructure/website/booking/templates/confirmation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |

Your Booking is Confirmed

14 | Go Back 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /infrastructure/website/booking/templates/delete_confirmation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |

Your Booking is successfully deleted

14 | Go Back 15 |
16 | 17 | -------------------------------------------------------------------------------- /infrastructure/website/booking/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |

Booking Page

14 | {{ res }} 15 | 16 | 21 | 22 |
23 | {% csrf_token %} 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 | 43 |
44 |
45 | 46 |
47 |
48 | 49 | 50 |
51 |
52 | 53 | 54 |
55 |
56 | 57 |
58 | 59 |
60 | 61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /infrastructure/website/booking/templates/update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 |
13 |

Update Page

14 | {{ res }} 15 | 16 |
17 | {% csrf_token %} 18 | 19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 | 44 |
45 |
46 | 47 | {{data.booking}} 48 | 49 |
50 |
51 | 52 |
53 | 54 | Delete 55 |
56 | 57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /infrastructure/website/booking/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /infrastructure/website/booking/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import home, create_new, update, delete 3 | 4 | urlpatterns = [ 5 | path('delete/', delete), 6 | path('update/', update), 7 | path('create-new', create_new), 8 | path('', home), 9 | ] 10 | -------------------------------------------------------------------------------- /infrastructure/website/booking/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import HttpResponse, render 2 | from datetime import datetime 3 | from booking_service.application.booking.booking_manager import BookingManager 4 | from booking_service.application.booking.booking_dto import * 5 | from booking_service.application.customers.customer_dto import CustomerDto 6 | from .repositories import BookingRepository 7 | from django.shortcuts import redirect 8 | 9 | def home(request): 10 | user_dto = UserDto(request.user.first_name, request.user.is_superuser) 11 | user_dto.id = request.user.id 12 | repository = BookingRepository() 13 | manager = BookingManager(repository) 14 | bookings = manager.get_bookings(user_dto) 15 | return render(request, 'index.html', {'bookings': bookings}) 16 | 17 | def create_new(request): 18 | checkin = datetime.strptime(request.POST.get('checkin'), "%Y-%m-%dT%H:%M") 19 | checkout = datetime.strptime(request.POST.get('checkout'),"%Y-%m-%dT%H:%M") 20 | 21 | customer_dto = get_customer_from_request(request) 22 | 23 | dto = BookingDto(checkin, checkout, customer_dto) 24 | repository = BookingRepository() 25 | manager = BookingManager(repository) 26 | res = manager.create_new_booking(dto) 27 | 28 | if res['code'] != 'SUCCESS': 29 | return render(request, 'index.html', {'res': res}) 30 | else: 31 | return render(request, 'confirmation.html') 32 | 33 | 34 | def update(request, id): 35 | user_dto = UserDto(request.user.first_name, request.user.is_superuser) 36 | repository = BookingRepository() 37 | manager = BookingManager(repository) 38 | 39 | if request.method == 'GET': 40 | resp = manager.get_booking_by_id(id, user_dto) 41 | if resp['code'] == 'SUCCESS': 42 | resp['checkin'] = resp['data'].checkin.strftime("%Y-%m-%dT%H:%M") 43 | resp['checkout'] = resp['data'].checkout.strftime("%Y-%m-%dT%H:%M") 44 | return render(request, 'update.html', resp) 45 | else: 46 | return render(request, 'index.html', {'res': resp['message']}) 47 | elif request.method == 'POST': 48 | checkin = datetime.strptime(request.POST.get('checkin'), "%Y-%m-%dT%H:%M") 49 | checkout = datetime.strptime(request.POST.get('checkout'),"%Y-%m-%dT%H:%M") 50 | 51 | customer_dto = get_customer_from_request(request) 52 | dto = BookingDto(checkin, checkout, customer_dto) 53 | dto.id = id 54 | repository = BookingRepository() 55 | manager = BookingManager(repository) 56 | res = manager.update_booking(dto) 57 | 58 | if res['code'] != 'SUCCESS': 59 | return render(request, 'index.html', {'res': res}) 60 | else: 61 | return render(request, 'confirmation.html') 62 | 63 | def delete(request, id): 64 | repository = BookingRepository() 65 | manager = BookingManager(repository) 66 | res = manager.delete_booking(id) 67 | if res['code'] == 'SUCCESS': 68 | return render(request, 'delete_confirmation.html') 69 | else: 70 | return HttpResponse(res['message']) 71 | 72 | def get_customer_from_request(request): 73 | name = request.POST.get('name') 74 | age = int(request.POST.get('age')) 75 | document = request.POST.get('document') 76 | email = request.POST.get('email') 77 | customer_dto = CustomerDto(name, age, document, email) 78 | return customer_dto 79 | -------------------------------------------------------------------------------- /infrastructure/website/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | sys.path.append("..") 6 | sys.path.append("../..") 7 | 8 | def main(): 9 | """Run administrative tasks.""" 10 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'website.settings') 11 | try: 12 | from django.core.management import execute_from_command_line 13 | except ImportError as exc: 14 | raise ImportError( 15 | "Couldn't import Django. Are you sure it's installed and " 16 | "available on your PYTHONPATH environment variable? Did you " 17 | "forget to activate a virtual environment?" 18 | ) from exc 19 | execute_from_command_line(sys.argv) 20 | 21 | 22 | if __name__ == '__main__': 23 | main() 24 | -------------------------------------------------------------------------------- /infrastructure/website/website/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gpzim98/DjangoFlaskCleanArchitecture/7032c76837dc3336382e9e5c4968fc6d417aed44/infrastructure/website/website/__init__.py -------------------------------------------------------------------------------- /infrastructure/website/website/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for website project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'website.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /infrastructure/website/website/settings.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | BASE_DIR = Path(__file__).resolve().parent.parent 4 | 5 | SECRET_KEY = 'django-insecure-%#1a7)0ty15$h#4_#^gu%rj7)*!=7ibw+)hn(1kb3_28h&bbxg' 6 | 7 | DEBUG = True 8 | 9 | ALLOWED_HOSTS = [] 10 | 11 | INSTALLED_APPS = [ 12 | 'django.contrib.admin', 13 | 'django.contrib.auth', 14 | 'django.contrib.contenttypes', 15 | 'django.contrib.sessions', 16 | 'django.contrib.messages', 17 | 'django.contrib.staticfiles', 18 | 'booking' 19 | ] 20 | 21 | MIDDLEWARE = [ 22 | 'django.middleware.security.SecurityMiddleware', 23 | 'django.contrib.sessions.middleware.SessionMiddleware', 24 | 'django.middleware.common.CommonMiddleware', 25 | 'django.middleware.csrf.CsrfViewMiddleware', 26 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 27 | 'django.contrib.messages.middleware.MessageMiddleware', 28 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 29 | ] 30 | 31 | ROOT_URLCONF = 'website.urls' 32 | 33 | TEMPLATES = [ 34 | { 35 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 36 | 'DIRS': [], 37 | 'APP_DIRS': True, 38 | 'OPTIONS': { 39 | 'context_processors': [ 40 | 'django.template.context_processors.debug', 41 | 'django.template.context_processors.request', 42 | 'django.contrib.auth.context_processors.auth', 43 | 'django.contrib.messages.context_processors.messages', 44 | ], 45 | }, 46 | }, 47 | ] 48 | 49 | WSGI_APPLICATION = 'website.wsgi.application' 50 | 51 | DATABASES = { 52 | 'default': { 53 | 'ENGINE': 'django.db.backends.sqlite3', 54 | 'NAME': BASE_DIR / 'db.sqlite3', 55 | } 56 | } 57 | 58 | AUTH_PASSWORD_VALIDATORS = [ 59 | { 60 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 61 | }, 62 | { 63 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 64 | }, 65 | { 66 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 67 | }, 68 | { 69 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 70 | }, 71 | ] 72 | 73 | 74 | LANGUAGE_CODE = 'en-us' 75 | 76 | TIME_ZONE = 'UTC' 77 | 78 | USE_I18N = True 79 | 80 | USE_TZ = True 81 | 82 | STATIC_URL = 'static/' 83 | 84 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 85 | -------------------------------------------------------------------------------- /infrastructure/website/website/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | 4 | urlpatterns = [ 5 | path('booking/', include('booking.urls')), 6 | path('admin/', admin.site.urls), 7 | ] 8 | -------------------------------------------------------------------------------- /infrastructure/website/website/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for website 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/4.1/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', 'website.settings') 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------