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
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
# iReporter2
This is a version 2 of the iReporter which uses databases
[![Build Status](https://travis-ci.org/IssaIan/iReporter2.svg?branch=develop)](https://travis-ci.org/IssaIan/iReporter2)
[![Coverage Status](https://coveralls.io/repos/github/IssaIan/iReporter2/badge.svg?branch=develop)](https://coveralls.io/github/IssaIan/iReporter2?branch=develop)


This is a version 2 of the iReporter app which uses databases.
iReporter is a digital platform for citizens to report corruption cases to relevant authorities. Users can also report on things that need government intervention.

## Installation
To install and run the project locally:

Clone the repo git clone https://github.com/IssaIan/iReporter2.git
cd into iReporter2/
virtualenv -p python3 env
source env/bin/activate
pip install -r requirements.txt
export FLASK_APP=run.py
flask run

## Api Endpoints

1. Create an incident: /api/v1/incidents
2. Edit a specific incident comment/description: /api/v1/incident_id/descrition
3. Edit a specific incident location: /api/v1/incident_id/location
4. Create a user: /api/v1/users
5. Get or update a specific user: /api/v1/users/user_id
44 changes: 41 additions & 3 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,52 @@
from flask import Flask, Blueprint
import os
from flask_jwt_extended import JWTManager
from instance.config import Config
import datetime
from flask import Flask, Blueprint, request, jsonify
from instance.config import app_config
from db_config import *
from db_config import create_tables
from app.api.v2 import version_two as v2

jwt = JWTManager()
timeout = datetime.timedelta(4000)


def create_app(config_name='testing'):
app = Flask(__name__, instance_relative_config=True)
app.url_map.strict_slashes = False
app.config.from_object(app_config['testing'])
app.config.from_pyfile('config.py')
app.config['JWT_SECRET_KEY'] = os.getenv("JWT_SECRET_KEY")
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timeout
jwt.init_app(app)
create_tables()
app.register_blueprint(v2)
return app

@app.errorhandler(404)
def invalid_endpoint(error=None):
"""Handle wrong endpoints """
return jsonify({
'Message': '{} is not a valid url'.format(request.url)
}), 404

@app.errorhandler(405)
def wrong_method(error=None):
"""Handle wrong methods for an endpoint """
request_method = request.method
return jsonify({
'Message': 'The {} method is not allowed for this endpoint'
.format(request_method)}), 405

@app.errorhandler(400)
def wrong_request(error=None):
"""Handle wrong requests for an endpoint """
return jsonify({
'Message': 'This endpoint supports JSON requests only.'}), 400

@app.errorhandler(500)
def server_error(error=None):
"""Handle internal server error for an endpoint """
return jsonify({
'Message': 'Verify if the request causes a server error'}), 500

return app
60 changes: 15 additions & 45 deletions app/api/v2/models/incidentmodels.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,36 @@
import psycopg2
from flask import jsonify, json
from flask_restful import request
from psycopg2.extras import RealDictCursor
from db_config import *


class IncidentModels():
def __init__(self):
self.incidents = init_db()

def save_incident(self, incident_id, created_by, typee, description, status, location, created_on ):

curr = self.incidents.cursor()
def save_incident(self, typee, description, status, location):
curr = self.incidents.cursor(cursor_factory=RealDictCursor)
curr.execute(
"INSERT INTO incidents(incident_id, created_by, type, description, status, \
location, created_on)VALUES(%s, %s, %s, %s, %s, %s, %s)",
(incident_id, created_by, typee, description, status, location, created_on))
"INSERT INTO incidents(type, description, status, location)VALUES(%s, %s, %s, %s)",
(typee, description, status, location))
self.incidents.commit()

def get_all(self):
curr = self.incidents.cursor()
curr = self.incidents.cursor(cursor_factory=RealDictCursor)
curr.execute("SELECT * FROM incidents")
rows = curr.fetchall()
incidentlist = []

for i, items in enumerate(rows):
incident_id, created_by, typee, description, status, location, created_on = items
data = dict(
incident_id = int(incident_id),
created_by = int(created_by),
type = typee,
description = description,
status = status,
location = location,
created_on = created_on
)
incidentlist.append(data)
incidentlist = curr.fetchall()
return incidentlist

def find_by_id(self, id):
curr = self.incidents.cursor()
curr.execute("SELECT FROM incidents WHERE incident_id = (%s);", (id,))
row = curr.fetchone()
incident = []
for items in enumerate(row):

incident_id, created_by, typee, description, status, location, created_on = items
data = dict(
incident_id = int(incident_id),
created_by = int(created_by),
type = typee,
description = description,
status = status,
location = location,
created_on = created_on
)
incident.append(data)
curr = self.incidents.cursor(cursor_factory=RealDictCursor)
curr.execute(
"SELECT * FROM incidents WHERE incident_id = {}".format(id))
incident = curr.fetchall()
return incident


def delete(self, id):
curr = self.incidents.cursor()
curr.execute("DELETE FROM incidents WHERE incident_id = (%s);",(id,))
self.find_by_id(id)
curr = self.incidents.cursor(cursor_factory=RealDictCursor)
curr.execute("DELETE FROM incidents WHERE incident_id = {}".format(id))
self.incidents.commit()



96 changes: 73 additions & 23 deletions app/api/v2/models/usermodels.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,88 @@
import psycopg2
import re
from flask_restful import request
from werkzeug.security import check_password_hash, generate_password_hash
from psycopg2.extras import RealDictCursor
from flask_jwt_extended import get_jwt_identity, create_access_token
from db_config import *


class UserModels():
def __init__(self):
self.users = init_db()

def save_user(self, user_id, first_name, last_name, username, email, date_created, password):
curr = self.users.cursor()
def save_user(self, first_name, last_name, username, email, phonenumber, password):
curr = self.users.cursor(cursor_factory=RealDictCursor)
curr.execute(
"INSERT INTO users(user_id, first_name, last_name, username, email, date_created, password)\
VALUES(%s, %s, %s, %s, %s, %s, %s)",
(user_id, first_name, last_name, username, email, date_created, password))
self.users.commit()
"INSERT INTO users(first_name, last_name, username, email, phonenumber, password)VALUES(%s, %s, %s, %s, %s, %s)",
(first_name, last_name, username, email, phonenumber, generate_password_hash(password)))
self.users.commit()

def get_all(self):
curr = self.users.cursor()
curr = self.users.cursor(cursor_factory=RealDictCursor)
curr.execute("SELECT * FROM users")
rows = curr.fetchall()
userlist = []

for i, items in enumerate(rows):
user_id, first_name, last_name, username, email, date_created, password = items
data = dict(
user_id = int(user_id),
first_name = first_name,
last_name = last_name,
username = username,
email = email,
date_created = date_created,
password = password
)
userlist.append(data)
userlist = curr.fetchall()
return userlist


def get_user_name(self, username):
curr = self.users.cursor(cursor_factory=RealDictCursor)

curr.execute(
"SELECT username FROM users WHERE username='{}'".format(username))
user = curr.fetchall()
return user

def get_email(self, email):
curr = self.users.cursor(cursor_factory=RealDictCursor)
curr.execute(
"SELECT * FROM users WHERE email='{}'".format(email))
row = curr.fetchone()
if row:
email = row.get('email')
return email
else:
return None

def validate_email(self, email):
valid = re.match(
"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email.strip())
if valid is None:
return False

return True

def get_id(self, username):
curr = self.users.cursor(cursor_factory=RealDictCursor)
curr.execute(
"SELECT user_id FROM users WHERE username='{}'".format(username))
user_id = curr.fetchone()
if user_id:
id = user_id.get('user_id')
return id
else:
return None

def check_password(self, username, password):
self.password = self.get_password(username)
match = check_password_hash(password, self.password)
return match

def get_password(self, username):
curr = self.users.cursor(cursor_factory=RealDictCursor)
curr.execute(
"SELECT password FROM users WHERE username='{}'".format(username))
pas = curr.fetchone()
if pas:
password = pas.get('password')
return password
else:
return None

def generate_jwt_token(self, username):
self.user_id = self.get_id(username)
token = create_access_token(identity=self.user_id)
return token

def user_login(self, username):
token = self.generate_jwt_token(username)
return token
54 changes: 25 additions & 29 deletions app/api/v2/views/incidentviews.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,61 @@
from flask_restful import Resource
from flask import jsonify, make_response, request
from app.api.v2.models.incidentmodels import IncidentModels
import datetime

class GetError():
def notFound(self):
return {
'Message':'Resource not found'
}, 404

class Incidents(Resource, GetError):
class Incidents(Resource):
def __init__(self):
self.db = IncidentModels()

def post(self):

data = request.get_json()
incident_id = data['incident_id']
created_by = data['created_by']
typee = data['typee']
description = data['description']
status = data['status']
location = data['location']
created_on = data['created_on']
self.db.save_incident(incident_id,created_by, typee,description, status, location, created_on)
created_on = datetime.datetime.now()

self.db.save_incident(typee, description, status, location)
return make_response(jsonify({
'Message' : 'Success'
'Message': 'Success'
}), 201)

def get(self):
result = self.db.get_all()
if result == []:
return self.notFound()
return jsonify({
'Message' : 'Record not found!'
}, 404)
else:
return make_response(jsonify(
{
'Message':'Records returned successfully',
'Data':result
'Message': 'Records returned successfully',
'Data': result
}
), 200)

class Incident(Resource, GetError):

class Incident(Resource):
def __init__(self):
self.db = IncidentModels()

def delete(self, incident_id):
self.db.delete(incident_id)
if True:
return {
'Message':'Successfully deleted'
}, 200
else:
return self.notFound()
return {
'Message': 'Successfully deleted'
}, 200

def get(self, incident_id):
incident = self.db.find_by_id(incident_id)

if incident == []:
return jsonify({
'Message' : 'Record not found!'
}, 404)
return make_response(jsonify({
'Message':'Incident returned successfully',
'Data':incident
'Message': 'Incident returned successfully',
'Data': incident
}), 200)



def update(self):
pass

Loading