Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions time_tracker_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@

api.add_namespace(time_entries_namespace.ns)

from time_tracker_api.project_types import project_types_namespace

api.add_namespace(project_types_namespace.ns)

from time_tracker_api.customers import customers_namespace

api.add_namespace(customers_namespace.ns)

"""
Error handlers
"""
Expand Down
33 changes: 33 additions & 0 deletions time_tracker_api/customers/customers_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from time_tracker_api.database import CRUDDao


class CustomerDao(CRUDDao):
pass


def create_dao() -> CustomerDao:
from time_tracker_api.sql_repository import db
from time_tracker_api.database import COMMENTS_MAX_LENGTH
from time_tracker_api.sql_repository import SQLCRUDDao
from sqlalchemy_utils import UUIDType
import uuid

class CustomerSQLModel(db.Model):
__tablename__ = 'customer'
id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4)
name = db.Column(db.String(50), unique=True, nullable=False)
description = db.Column(db.String(COMMENTS_MAX_LENGTH), unique=False, nullable=False)
deleted = db.Column(UUIDType(binary=False), default=uuid.uuid4)
tenant_id = db.Column(UUIDType(binary=False), default=uuid.uuid4)

def __repr__(self):
return '<Customer %r>' % self.name

def __str___(self):
return "the customer \"%s\"" % self.name

class CustomerSQLDao(SQLCRUDDao):
def __init__(self):
SQLCRUDDao.__init__(self, CustomerSQLModel)

return CustomerSQLDao()
101 changes: 101 additions & 0 deletions time_tracker_api/customers/customers_namespace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from faker import Faker
from flask_restplus import Namespace, Resource, fields
from flask_restplus._http import HTTPStatus

from time_tracker_api.api import audit_fields
from time_tracker_api.customers.customers_model import create_dao

faker = Faker()

ns = Namespace('customers', description='API for customers')

# Customer Model
customer_input = ns.model('CustomerInput', {
'name': fields.String(
required=True,
title='Name',
max_length=50,
description='Name of the customer',
example=faker.company(),
),
'description': fields.String(
title='Description',
max_length=250,
description='Description about the customer',
example=faker.paragraph(),
),
'tenant_id': fields.String(
required=True,
title='Identifier of Tenant',
description='Tenant this customer belongs to',
example=faker.uuid4(),
),
})

customer_response_fields = {
'id': fields.String(
readOnly=True,
required=True,
title='Identifier',
description='The unique identifier',
example=faker.uuid4(),
)
}
customer_response_fields.update(audit_fields)

customer = ns.inherit(
'Customer',
customer_input,
customer_response_fields
)

customer_dao = create_dao()


@ns.route('')
class Customers(Resource):
@ns.doc('list_customers')
@ns.marshal_list_with(customer)
def get(self):
"""List all customers"""
return customer_dao.get_all()

@ns.doc('create_customer')
@ns.response(HTTPStatus.CONFLICT, 'This customer already exists')
@ns.response(HTTPStatus.BAD_REQUEST, 'Invalid format or structure '
'of the attributes of the customer')
@ns.expect(customer_input)
@ns.marshal_with(customer, code=HTTPStatus.CREATED)
def post(self):
"""Create a customer"""
return customer_dao.create(ns.payload), HTTPStatus.CREATED


@ns.route('/<string:id>')
@ns.response(HTTPStatus.NOT_FOUND, 'This customer does not exist')
@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The data has an invalid format')
@ns.param('id', 'The customer identifier')
class Customer(Resource):
@ns.doc('get_customer')
@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The id has an invalid format')
@ns.marshal_with(customer)
def get(self, id):
"""Get a customer"""
return customer_dao.get(id)

@ns.doc('update_customer')
@ns.response(HTTPStatus.BAD_REQUEST, 'Invalid format or structure '
'of the attributes of the customer')
@ns.response(HTTPStatus.CONFLICT, 'A customer already exists with this new data')
@ns.expect(customer_input)
@ns.marshal_with(customer)
def put(self, id):
"""Update a customer"""
return customer_dao.update(id, ns.payload)

@ns.doc('delete_customer')
@ns.response(HTTPStatus.NO_CONTENT, 'Customer successfully deleted')
def delete(self, id):
"""Delete a customer"""
customer_dao.delete(id)
return None, HTTPStatus.NO_CONTENT
35 changes: 35 additions & 0 deletions time_tracker_api/project_types/project_types_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from time_tracker_api.database import CRUDDao


class ProjectTypeDao(CRUDDao):
pass


def create_dao() -> ProjectTypeDao:
from time_tracker_api.sql_repository import db
from time_tracker_api.database import COMMENTS_MAX_LENGTH
from time_tracker_api.sql_repository import SQLCRUDDao
from sqlalchemy_utils import UUIDType
import uuid

class ProjectTypeSQLModel(db.Model):
__tablename__ = 'project_type'
id = db.Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4)
name = db.Column(db.String(50), unique=True, nullable=False)
description = db.Column(db.String(COMMENTS_MAX_LENGTH), unique=False, nullable=False)
parent_id = db.Column(UUIDType(binary=False), default=uuid.uuid4)
customer_id = db.Column(UUIDType(binary=False), default=uuid.uuid4)
deleted = db.Column(UUIDType(binary=False), default=uuid.uuid4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the other SQL models should inherit from AuditedSQLModel. AuditedSQLModel should also contain the common fields, as deleted and if possible add to it an auto-calculated attribute called visible which is True if the next condition is:

self.deleted == None

tenant_id = db.Column(UUIDType(binary=False), default=uuid.uuid4)

def __repr__(self):
return '<ProjectType %r>' % self.name

def __str___(self):
return "the project type \"%s\"" % self.name

class ProjectTypeSQLDao(SQLCRUDDao):
def __init__(self):
SQLCRUDDao.__init__(self, ProjectTypeSQLModel)

return ProjectTypeSQLDao()
111 changes: 111 additions & 0 deletions time_tracker_api/project_types/project_types_namespace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from faker import Faker
from flask_restplus import Namespace, Resource, fields
from flask_restplus._http import HTTPStatus

from time_tracker_api.api import audit_fields
from time_tracker_api.project_types.project_types_model import create_dao

faker = Faker()

ns = Namespace('project-types', description='API for project types')

# ProjectType Model
project_type_input = ns.model('ProjectTypeInput', {
'name': fields.String(
required=True,
title='Name',
max_length=50,
description='Name of the project type',
example=faker.company(),
),
'description': fields.String(
title='Description',
max_length=250,
description='Description about the project type',
example=faker.paragraph(),
),
'customer_id': fields.String(
title='Identifier of the Customer',
description='Customer this project type belongs to',
example=faker.uuid4(),
),
'tenant_id': fields.String(
required=True,
title='Identifier of Tenant',
description='Tenant this project type belongs to',
example=faker.uuid4(),
),
'parent_id': fields.String(
title='Identifier of Parent of the project type',
description='Defines a self reference of the model ProjectType',
example=faker.uuid4(),
)
})

project_type_response_fields = {
'id': fields.String(
readOnly=True,
required=True,
title='Identifier',
description='The unique identifier',
example=faker.uuid4(),
)
}
project_type_response_fields.update(audit_fields)

project_type = ns.inherit(
'ProjectType',
project_type_input,
project_type_response_fields
)

project_type_dao = create_dao()


@ns.route('')
class ProjectTypes(Resource):
@ns.doc('list_project_types')
@ns.marshal_list_with(project_type)
def get(self):
"""List all project types"""
return project_type_dao.get_all()

@ns.doc('create_project_type')
@ns.response(HTTPStatus.CONFLICT, 'This project type already exists')
@ns.response(HTTPStatus.BAD_REQUEST, 'Invalid format or structure '
'of the attributes of the project type')
@ns.expect(project_type_input)
@ns.marshal_with(project_type, code=HTTPStatus.CREATED)
def post(self):
"""Create a project type"""
return project_type_dao.create(ns.payload), HTTPStatus.CREATED


@ns.route('/<string:id>')
@ns.response(HTTPStatus.NOT_FOUND, 'This project type does not exist')
@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The data has an invalid format')
@ns.param('id', 'The project type identifier')
class ProjectType(Resource):
@ns.doc('get_project_type')
@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The id has an invalid format')
@ns.marshal_with(project_type)
def get(self, id):
"""Get a project type"""
return project_type_dao.get(id)

@ns.doc('update_project_type')
@ns.response(HTTPStatus.BAD_REQUEST, 'Invalid format or structure '
'of the attributes of the project type')
@ns.response(HTTPStatus.CONFLICT, 'A project type already exists with this new data')
@ns.expect(project_type_input)
@ns.marshal_with(project_type)
def put(self, id):
"""Update a project type"""
return project_type_dao.update(id, ns.payload)

@ns.doc('delete_project_type')
@ns.response(HTTPStatus.NO_CONTENT, 'Project Type successfully deleted')
def delete(self, id):
"""Delete a project type"""
project_type_dao.delete(id)
return None, HTTPStatus.NO_CONTENT