Skip to content

Commit 92fd1df

Browse files
authored
Merge pull request #3 from ioet/feature/create-projects-endpoints#1
Close #1 create projects endpoints
2 parents 306c54e + 3b026af commit 92fd1df

File tree

7 files changed

+174
-25
lines changed

7 files changed

+174
-25
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,11 @@ __pycache__/
1212
.vs
1313
.trash
1414

15+
# Jetbrans IDEs
16+
.idea
17+
1518
# virtual environments
16-
.venv
19+
.venv
20+
21+
# Files generated for development
22+
.env

README.md

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
# time-tracker-api
22

3-
## Set new environment in Windows
4-
- `mkdir .venv`
5-
- `python -m venv .venv`
6-
- `.venv\Scripts\activate.bat`
7-
- `pip install -r requirements/prod.txt`
8-
9-
## Example usage in Windows
10-
- `set FLASK_APP=time_tracker_api`
11-
- `flask run`
3+
## Setup
4+
5+
- Create and activate the environment,
6+
7+
In Windows:
8+
9+
```
10+
python -m venv .venv
11+
.venv\Scripts\activate.bat
12+
```
13+
14+
In Unix based operative systems:
15+
```
16+
virtualenv .venv
17+
source .venv/bin/activate
18+
```
19+
- Install the requirements:
20+
```
21+
python3 -m pip install -r requirements/prod.txt
22+
```
23+
Remember to do it with Python 3.
24+
25+
## How to use it
26+
- Set the env var `FLASK_APP` to `time_tracker_api` and start the app:
27+
28+
In Windows
29+
```
30+
set FLASK_APP=time_tracker_api
31+
flask run
32+
```
33+
In Unix based operative systems:
34+
```
35+
export FLASK_APP=time_tracker_api
36+
flask run
37+
```
38+
1239
- Open `http://127.0.0.1:5000/` in a browser

requirements/prod.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
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+
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)