Skip to content

Commit 667d626

Browse files
committed
Fixes #1 Create model for projects
1 parent 306c54e commit 667d626

File tree

7 files changed

+137
-16
lines changed

7 files changed

+137
-16
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@ __pycache__/
1212
.vs
1313
.trash
1414

15+
# Jetbrans IDEs
16+
.idea
17+
1518
# virtual environments
1619
.venv

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
- `pip install -r requirements/prod.txt`
88

99
## Example usage in Windows
10-
- `set FLASK_APP=time_tracker_api`
10+
- `export FLASK_APP=time_tracker_api` or `set FLASK_APP=time_tracker_api`
1111
- `flask run`
1212
- Open `http://127.0.0.1:5000/` in a browser

requirements/prod.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
Flask==1.1.1
22
flask-restplus==0.13.0
33
flake8==3.7.9
4-
Werkzeug==0.16.1
4+
Werkzeug==0.16.1
5+
wrapt==1.11.2
6+
zipp==3.1.0
7+
gunicorn==20.0.4

run.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env python3
2+
"""
3+
This file is needed by gunicorn to run
4+
"""
5+
from time_tracker_api import create_app
6+
7+
app = create_app()
8+
print("BPM Projects API server was created")

time_tracker_api/errors.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class MissingResource(Exception):
2+
"""
3+
Errors related to missing resource in the system
4+
"""
5+
pass
6+
7+
8+
class InvalidInput(Exception):
9+
"""
10+
Errors related to an invalid input coming from the user
11+
"""
12+
pass
13+
14+
15+
class InvalidMatch(Exception):
16+
"""
17+
Errors related to an invalid match during a search
18+
"""
19+
pass
Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
from flask_restplus import Namespace
2-
from flask_restplus import Resource
1+
from flask_restplus import Namespace, Resource, abort, inputs
2+
from .projects_model import project_dao
3+
from time_tracker_api.errors import MissingResource
34

45
ns = Namespace('projects', description='Api for resource `Projects`')
56

6-
77
@ns.route('/')
88
class Projects(Resource):
99
@ns.doc('list_projects')
1010
def get(self):
1111
"""List all projects"""
12-
print("List all projects")
13-
return {}, 200
12+
return project_dao.get_all(), 200
1413

1514
@ns.doc('create_project')
1615
def post(self):
1716
"""Create a project"""
18-
print("List all projects")
19-
return {}, 201
17+
return project_dao.create(ns.payload), 201
18+
19+
project_update_parser = ns.parser()
20+
project_update_parser.add_argument('active',
21+
type=inputs.boolean,
22+
location='form',
23+
required=True,
24+
help='Is the project active?')
2025

2126

2227
@ns.route('/<string:uid>')
@@ -26,26 +31,29 @@ class Project(Resource):
2631
@ns.doc('get_project')
2732
def get(self, uid):
2833
"""Retrieve a project"""
29-
print("Retrieve Project")
30-
return {}
34+
return project_dao.get(uid)
3135

3236
@ns.doc('delete_project')
3337
@ns.response(204, 'Project deleted')
3438
def delete(self, uid):
3539
"""Deletes a project"""
36-
print("Delete Project")
40+
project_dao.delete(uid)
3741
return None, 204
3842

3943
@ns.doc('put_project')
4044
def put(self, uid):
4145
"""Create or replace a project"""
42-
print("Create or Replace Project")
43-
return {}
46+
return project_dao.update(uid, ns.payload)
4447

4548
@ns.doc('update_project_status')
4649
@ns.param('uid', 'The project identifier')
4750
@ns.response(204, 'State of the project successfully updated')
4851
def post(self, uid):
4952
"""Updates a project using form data"""
50-
print("Update Project using form data")
51-
return {}
53+
try:
54+
update_data = project_update_parser.parse_args()
55+
return project_dao.update(uid, update_data), 200
56+
except ValueError:
57+
abort(code=400)
58+
except MissingResource as e:
59+
abort(message=str(e), code=404)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from time_tracker_api.errors \
2+
import MissingResource, InvalidInput, InvalidMatch
3+
4+
5+
class InMemoryProjectDAO(object):
6+
def __init__(self):
7+
self.counter = 0
8+
self.projects = []
9+
10+
def get_all(self):
11+
return self.projects
12+
13+
def get(self, uid):
14+
for project in self.projects:
15+
if project.get('uid') == uid:
16+
return project
17+
raise MissingResource("Project '%s' not found" % uid)
18+
19+
def create(self, project):
20+
self.counter += 1
21+
project['uid'] = str(self.counter)
22+
self.projects.append(project)
23+
return project
24+
25+
def update(self, uid, data):
26+
project = self.get(uid)
27+
if project:
28+
project.update(data)
29+
return project
30+
else:
31+
raise MissingResource("Project '%s' not found" % uid)
32+
33+
def delete(self, uid):
34+
if uid:
35+
project = self.get(uid)
36+
self.projects.remove(project)
37+
38+
def flush(self):
39+
self.projects.clear()
40+
41+
def search(self, search_criteria):
42+
matching_projects = self.select_matching_projects(search_criteria)
43+
44+
if len(matching_projects) > 0:
45+
return matching_projects
46+
else:
47+
raise InvalidMatch("No project matched the specified criteria")
48+
49+
def select_matching_projects(self, user_search_criteria):
50+
search_criteria = {k: v for k, v
51+
in user_search_criteria.items()
52+
if v is not None}
53+
54+
def matches_search_string(search_str, project):
55+
return search_str in project['comments'] or \
56+
search_str in project['short_name']
57+
58+
if not search_criteria:
59+
raise InvalidInput("No search criteria specified")
60+
61+
search_str = search_criteria.get('search_string')
62+
if search_str:
63+
matching_projects = [p for p
64+
in self.projects
65+
if matches_search_string(search_str, p)]
66+
else:
67+
matching_projects = self.projects
68+
69+
is_active = search_criteria.get('active')
70+
if is_active is not None:
71+
matching_projects = [p for p
72+
in matching_projects
73+
if p['active'] is is_active]
74+
75+
return matching_projects
76+
77+
78+
# Instances
79+
# TODO Create an strategy to create other types of DAO
80+
project_dao = InMemoryProjectDAO()

0 commit comments

Comments
 (0)