diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/StaffProfileDemo.iml b/.idea/StaffProfileDemo.iml
new file mode 100644
index 0000000..7eb1baf
--- /dev/null
+++ b/.idea/StaffProfileDemo.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..92f079a
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..f6104af
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..41b3889
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Test/__init__.py b/Test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Test/app_test.py b/Test/app_test.py
new file mode 100644
index 0000000..8a37741
--- /dev/null
+++ b/Test/app_test.py
@@ -0,0 +1,44 @@
+import unittest
+from app import app
+
+
+class BasicTestsSetup(unittest.TestCase):
+
+ def setUp(self):
+ app.config['TESTING'] = True
+ app.config['WTF_CSRF_ENABLED'] = False
+ app.config['DEBUG'] = False
+ self.app = app.test_client()
+ self.assertEqual(app.debug, False)
+
+ # executed after each test
+ def tearDown(self):
+ pass
+
+
+class TestCaseExamples(unittest.TestCase):
+
+ def test_home_status(self):
+ tester = app.test_client(self)
+ response = tester.get('/', content_type='html/text')
+ self.assertEqual(response.status_code, 200) # (test if the page renders and response is correct )
+
+ def test_home_content(self):
+ tester = app.test_client(self)
+ response = tester.get('/', content_type='html/text')
+ self.assertTrue(b'\n\n
\n \n Demo \n' in response.data) # passed the test
+
+ def test_dashboard(self):
+ tester = app.test_client(self)
+ response = tester.get('/dashboard', content_type='html/text')
+ self.assertTrue(b'\n\n \n \n \n' in response.data) # passed the test
+
+ def test_report_id(self):
+ tester = app.test_client(self)
+ response = tester.get('/issue/1', content_type='html/text')
+ self.assertTrue(b'Issue Details ' in response.data) # passed the test
+
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Test/test_issue.py b/Test/test_issue.py
new file mode 100644
index 0000000..b09edc0
--- /dev/null
+++ b/Test/test_issue.py
@@ -0,0 +1,32 @@
+import unittest
+from issue import Issue
+
+
+class TestIssue(unittest.TestCase):
+ def setUp(self):
+ self.__issue = Issue()
+
+ def test_issue_has_title(self):
+ self.__issue.set_title("problem with the bin")
+ title = self.__issue.get_title()
+ self.assertEqual(title, "problem with the bin")
+
+ def test_issue_has_type(self):
+ self.__issue.set_issue_type("bins")
+ type = self.__issue.get_issue_type()
+ self.assertEqual(type, "bins")
+
+ def test_issue_has_description(self):
+ self.__issue.set_description("there is a bin left outside 2 days before collection day")
+ description = self.__issue.get_description()
+ self.assertEqual(description, "there is a bin left outside 2 days before collection day")
+
+ def test_issue_has_postcode(self):
+ self.__issue.set_postcode("CV24EP")
+ postcode = self.__issue.get_postcode()
+ self.assertEqual(postcode, "CV24EP")
+
+ def test_issue_has_time(self):
+ self.__issue.set_time("03/04/2021 21:14")
+ the_time = self.__issue.get_time()
+ self.assertEqual(the_time, "03/04/2021 21:14")
diff --git a/Test/test_manager.py b/Test/test_manager.py
new file mode 100644
index 0000000..cc3596f
--- /dev/null
+++ b/Test/test_manager.py
@@ -0,0 +1,24 @@
+# Currently there is no class manager. In Composite pattern
+# the manager is part of "staff". Perhaps, this could grow into a
+# separate class on its own
+
+
+# import unittest
+# from manager import Manager
+#
+#
+# class TestManager(unittest.TestCase):
+#
+# def setUp(self):
+# print("setup")
+# self.__manager = Manager()
+# self.__email = ""
+#
+#
+# def tearDown(self):
+# print("teardown")
+# del self.__manager
+#
+#
+# if __name__ == '__main__':
+# unittest.main()
diff --git a/Test/test_messageboard.py b/Test/test_messageboard.py
new file mode 100644
index 0000000..8d2eda5
--- /dev/null
+++ b/Test/test_messageboard.py
@@ -0,0 +1,14 @@
+import unittest
+from messageboard import MessageController
+import datetime
+
+
+class TestMessageController(unittest.TestCase):
+ def setUp(self):
+ self.__message_controller = MessageController()
+
+ # MessageController displays message correctly
+ def test_issue_has_title(self):
+ message = self.__message_controller.display_message("this is a test message")
+ now = datetime.datetime.now()
+ self.assertEqual(message, now.strftime("%Y-%m-%d %H:%M:%S") + " " + "this is a test message")
diff --git a/Test/test_staff.py b/Test/test_staff.py
new file mode 100644
index 0000000..d3d35bc
--- /dev/null
+++ b/Test/test_staff.py
@@ -0,0 +1,39 @@
+import unittest
+from staff import Staff
+
+
+class TestStaff(unittest.TestCase):
+
+ # standard built in method "setUp" for testing
+ def setUp(self):
+ self.__user = Staff()
+ self.__user.set_role("staff")
+ self.__user.set_email("staff@mail.com")
+ self.__user.add_manager("manager@mail.com")
+
+ # test if role is correct
+ def test_get_role(self):
+ test_result = self.__user.get_role()
+ self.assertEqual(test_result, "staff")
+
+ # test if email is correct
+ def test_get_email(self):
+ test_result = self.__user.get_email()
+ self.assertEqual(test_result, "staff@mail.com")
+
+ # test that it doesn't have "managers" composite pattern
+ def test_phone_is_null(self):
+ # use built in hasattr() to check if attribute
+ # get_phone exist
+ test_result = hasattr(self.__user, "get_phone")
+ self.assertIs(test_result, False)
+
+ # test that it doesn't have "managers" composite pattern
+ def test_manager_exists(self):
+ test_result = self.__user.get_managers_email()
+ self.assertEqual(test_result, ["manager@mail.com"])
+
+ def test_manager_add(self):
+ self.__user.add_manager("manager2@mail.com")
+ test_result = self.__user.get_managers_email()
+ self.assertEqual(test_result, ["manager@mail.com","manager2@mail.com"])
\ No newline at end of file
diff --git a/Test/test_user.py b/Test/test_user.py
new file mode 100644
index 0000000..c6f543c
--- /dev/null
+++ b/Test/test_user.py
@@ -0,0 +1,35 @@
+import unittest
+from user import User
+
+
+class TestUser(unittest.TestCase):
+
+ # standard built in method "setUp" for testing
+ def setUp(self):
+ self.__user = User()
+ self.__user.set_role("user")
+ self.__user.set_email("test@mail.com")
+ self.__user.set_phone("07427730650")
+
+ # test if role is correct
+ def test_get_role(self):
+ test_result = self.__user.get_role()
+ self.assertEqual(test_result, "user")
+
+ # test if email is correct
+ def test_get_email(self):
+ test_result = self.__user.get_email()
+ self.assertEqual(test_result, "test@mail.com")
+
+ # test if phone exists and is correct
+ def test_phone_exists(self):
+ test_result = self.__user.get_phone()
+ self.assertIsNotNone(test_result)
+ self.assertEqual(test_result, "07427730650")
+
+ # test that it doesn't have "managers" composite pattern
+ def test_has_not_manager(self):
+ # use built in hasattr() to check if attribute
+ # get_managers exist
+ test_result = hasattr(self.__user, "get_managers")
+ self.assertIs(test_result, False)
\ No newline at end of file
diff --git a/Test/test_user_profile.py b/Test/test_user_profile.py
new file mode 100644
index 0000000..3fd1092
--- /dev/null
+++ b/Test/test_user_profile.py
@@ -0,0 +1,36 @@
+import unittest
+from user_profile import Profile
+
+
+class TestProfile(unittest.TestCase):
+
+ def setUp(self):
+ self.__user = Profile()
+
+ # we test if attributes exist
+ def test_has_get_email(self):
+ # use built in hasattr() to check if attribute
+ # get_managers exist
+ test_result = hasattr(self.__user, "get_email")
+ self.assertIs(test_result, True)
+
+ # we test if attributes exist
+ def test_has_set_email(self):
+ # use built in hasattr() to check if attribute
+ # get_managers exist
+ test_result = hasattr(self.__user, "set_email")
+ self.assertIs(test_result, True)
+
+ # we test if attributes exist
+ def test_has_get_role(self):
+ # use built in hasattr() to check if attribute
+ # get_managers exist
+ test_result = hasattr(self.__user, "get_role")
+ self.assertIs(test_result, True)
+
+ # we test if attributes exist
+ def test_has_get_role(self):
+ # use built in hasattr() to check if attribute
+ # get_managers exist
+ test_result = hasattr(self.__user, "set_role")
+ self.assertIs(test_result, True)
\ No newline at end of file
diff --git a/Test/test_userfactory.py b/Test/test_userfactory.py
new file mode 100644
index 0000000..9cc0904
--- /dev/null
+++ b/Test/test_userfactory.py
@@ -0,0 +1,17 @@
+import unittest
+from userfactory import UserFactory
+
+
+class TestUserFactory(unittest.TestCase):
+ def setUp(self):
+ self.__factory = UserFactory()
+
+ def test_factory_creates_staff(self):
+ staff = self.__factory.factory("staff")
+ role = staff.get_role()
+ self.assertEqual(role, "staff")
+
+ def test_factory_creates_user(self):
+ staff = self.__factory.factory("user")
+ role = staff.get_role()
+ self.assertEqual(role, "user")
\ No newline at end of file
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..4d57ce1
--- /dev/null
+++ b/app.py
@@ -0,0 +1,203 @@
+""" This is the app"""
+from flask import Flask, render_template, request, make_response
+from flaskext.mysql import MySQL
+from pip._vendor import requests
+from werkzeug.utils import redirect
+
+from userfactory import UserFactory
+from staff import Staff
+from flask_mail import Mail, Message
+from datetime import date
+import json
+
+mysql = MySQL()
+
+# initializing a variable of Flask
+app = Flask(__name__, template_folder="templates")
+
+# MySQL configurations
+app.config['MYSQL_DATABASE_USER'] = 'root'
+app.config['MYSQL_DATABASE_PASSWORD'] = ''
+app.config['MYSQL_DATABASE_DB'] = 'data_staff'
+app.config['MYSQL_DATABASE_HOST'] = 'localhost'
+
+# email set-up
+app.config['MAIL_SERVER'] = 'smtp.gmail.com'
+app.config['MAIL_PORT'] = 465
+app.config['MAIL_USERNAME'] = 'contact.forkswap@gmail.com'
+app.config['MAIL_PASSWORD'] = 'hashedpassword'
+app.config['MAIL_USE_TLS'] = False
+app.config['MAIL_USE_SSL'] = True
+
+mysql.init_app(app)
+mail = Mail(app)
+mail.init_app(app)
+
+the_list = [] # a list of StaffProject objects
+
+
+@app.route('/')
+def landing_page():
+ return render_template('index.html')
+
+
+@app.route('/sign-in', methods=['GET', 'POST'])
+def sign_in():
+ if request.method == 'GET':
+ return render_template('signin.html')
+ else:
+ con = mysql.connect() # set up database connection
+ cur = con.cursor()
+ email = request.form['email']
+ password = request.form['password']
+
+ cur.execute('SELECT * FROM users WHERE email=%s AND password=%s', [email, password])
+ print("retrieve the data from the database")
+ rows = cur.fetchall()
+ con.commit()
+
+ # Group of Four: FACTORY pattern
+ the_user = UserFactory().factory(rows[0][3])
+
+ if len(rows) != 0: # necessary check for db non indexed / null values
+ if the_user.get_role() == "staff": # factory pattern continued
+ resp = redirect("/dashboard")
+ resp.set_cookie('loggedInToken', "staff") # instead of staff on main we would have a session token
+ return resp
+ elif the_user.get_role() == "user": # factory pattern continued
+ resp = make_response(render_template('index.html'))
+ resp.set_cookie('loggedInToken', "user") # instead of user on main we would have a session token
+ return resp
+
+@app.route('/dashboard')
+def dashboard():
+ loggedInToken = request.cookies.get('loggedInToken')
+ if loggedInToken == "staff":
+ con = mysql.connect() # set up database connection
+ cur = con.cursor()
+ cur.execute('SELECT * FROM issues')
+ rows = cur.fetchall()
+ con.commit()
+ print(rows)
+ return render_template('dashboard.html', rows=rows)
+ else:
+ return render_template('signin.html')
+
+@app.route('/report-issue')
+def report_issue():
+ loggedInToken = request.cookies.get('loggedInToken')
+ if loggedInToken == "user":
+ return render_template('report.html')
+ else:
+ return render_template('signin.html')
+
+
+@app.route('/issue', methods=['POST'])
+def issue():
+ if request.method == 'POST':
+ con = mysql.connect() # set up database connection
+ cur = con.cursor()
+ title = request.form['title']
+ query = request.form['query']
+ description = request.form['description']
+ postcode = request.form['postcode']
+ today = date.today()
+
+ cur.execute('INSERT INTO issues (title, type_of_issue, description, postcode, time, userId)'
+ 'VALUES( %s, %s, %s, %s, %s, %s)',
+ (title, query, description, postcode, today, 1))
+ con.commit()
+ con.close()
+ return render_template('index.html')
+
+@app.route('/issue/', methods=['GET', 'POST'])
+def issue_detailed(id):
+ if request.method == 'GET':
+ con = mysql.connect() # set up database connection
+ cur = con.cursor()
+ cur.execute('SELECT * FROM issues WHERE id=%s', [id])
+ rows = cur.fetchall()
+ con.commit()
+ title = rows[0][1]
+ query = rows[0][2]
+ description = rows[0][3]
+ postcode = rows[0][4]
+ date_var = rows[0][5]
+ userid = rows[0][6]
+ solved = rows[0][7]
+
+ # API implementation
+ location = requests.get('https://geocode.search.hereapi.com/v1/geocode?apikey=9Ps1jjtBfqi8UrDg3V1tpKnAqbdLUI6KqhY3NU062K4&q='+postcode)
+ location = json.loads(location.text)["items"][0]["title"]
+ cur.execute('SELECT * FROM users WHERE id=%s', rows[0][6])
+ rows = cur.fetchall()
+ cur.execute('SELECT * FROM messages WHERE issue_id=%s', id)
+ rows2 = cur.fetchall()
+ message = ''
+ if rows2:
+ message = rows2[0][2]
+
+ staff = Staff()
+
+ # GoF composite pattern -----------------------
+ cur.execute('SELECT * FROM managers')
+ rows3 = cur.fetchall()
+ manager = Staff()
+ manager.set_email(rows3[0][1])
+ staff.add_manager(manager)
+ manager_info = staff.get_managers_email()
+ con.close()
+ return render_template("report_details.html", title=title, query=query, description=description, location=location, date=date_var, phone=rows[0][4], id=id, solved=solved, userid = userid, messages = message, manager_info = rows3[0][1])
+ else:
+ con = mysql.connect()
+ cur = con.cursor()
+ cur.execute('UPDATE issues SET solved=1 WHERE id=%s', (id))
+ con.commit()
+ con.close()
+ return redirect("/dashboard")
+
+
+@app.route('/delete/', methods=['POST'])
+def delete(id):
+ if request.method == 'POST':
+ try:
+ con = mysql.connect()
+ cur = con.cursor()
+ cur.execute('DELETE FROM Issues WHERE id=%s', id)
+ con.commit()
+ finally:
+ con.close()
+ return redirect("/dashboard")
+
+@app.route('/email//', methods=['POST'])
+def email(id, issue):
+ if request.method == 'POST':
+ con = mysql.connect()
+ cur = con.cursor()
+ emailText = request.form['emailText']
+ cur.execute('SELECT * FROM users WHERE id=%s', id)
+ rows = cur.fetchall()
+ con.commit()
+ receiver = rows[0][1]
+
+ # Group of Four: MEDIATOR PATTERN
+ staff = Staff()
+ message = staff.send_message(emailText)
+
+ # Add message to db
+ cur.execute('INSERT INTO messages (issue_id, message)'
+ 'VALUES( %s, %s)',
+ (issue, message))
+ con.commit()
+ # email
+ msg = Message("Update on Council Matter",
+ sender="contact.forkswap@gmail.com",
+ recipients=[receiver])
+ msg.body = emailText;
+ mail.send(msg)
+ con.close()
+ return redirect("/dashboard")
+
+
+if __name__ == "__main__":
+ app.run()
diff --git a/issue.py b/issue.py
new file mode 100644
index 0000000..2f1e672
--- /dev/null
+++ b/issue.py
@@ -0,0 +1,41 @@
+""" This is the entity, Issue"""
+
+
+class Issue:
+
+ def __init__(self): # a method to create objects
+ self.__title = "" # private attribute
+ self.__typeOfIssue = "" # private attribute
+ self.__description = "" # private attribute
+ self.__postcode = "" # private attribute
+ self.__time = "" # private attribute
+
+ def get_title(self): # get method
+ return self.__title
+
+ def set_title(self, title): # set method
+ self.__title = title
+
+ def get_issue_type(self): # get method
+ return self.__typeOfIssue
+
+ def set_issue_type(self, issue): # set method
+ self.__typeOfIssue = issue
+
+ def get_description(self): # get method
+ return self.__description
+
+ def set_description(self, description): # set method
+ self.__description = description
+
+ def get_postcode(self): # get method
+ return self.__postcode
+
+ def set_postcode(self, postcode): # set method
+ self.__postcode = postcode
+
+ def get_time(self): # get method
+ return self.__time
+
+ def set_time(self, time): # set method
+ self.__time = time
diff --git a/messageboard.py b/messageboard.py
new file mode 100644
index 0000000..5a4dbad
--- /dev/null
+++ b/messageboard.py
@@ -0,0 +1,12 @@
+""" This is an entity, MessageBoard"""
+import datetime
+
+
+# Group of Four: Mediator Pattern
+class MessageController(object): # a static class
+
+ @staticmethod
+ def display_message(text_to_display): # a static method
+ now = datetime.datetime.now()
+ displayable_message = now.strftime("%Y-%m-%d %H:%M:%S") + " " + text_to_display
+ return displayable_message
diff --git a/staff.py b/staff.py
new file mode 100644
index 0000000..dbcf5ca
--- /dev/null
+++ b/staff.py
@@ -0,0 +1,46 @@
+""" This is the entity, Staff"""
+from messageboard import MessageController
+from user_profile import Profile
+
+
+class Staff(Profile):
+
+ def __init__(self): # a method to create objects
+ self.__email = ""
+ self.__role = "staff"
+ self.__managers = [] # Group of Four composite pattern
+
+ # Group of Four Composite Pattern
+ def add_manager(self, manager):
+ self.__managers.append(manager)
+ # self.__managers.append(manager)
+
+ def remove_manager(self, manager):
+ self.__managers.remove(manager)
+
+ def get_managers(self):
+ return self.__managers
+
+ def get_managers_email(self):
+ emails = []
+ for member in self.__managers:
+ emails.append(member)
+ return emails
+ # ----------------------------------------------------
+
+ def get_email(self): # get method
+ return self.__email
+
+ def set_email(self, mail): # set method
+ self.__email = mail
+
+ def get_role(self):
+ return self.__role
+
+ def set_role(self, role):
+ self.__role = role
+
+ # Group of Four: Mediator pattern applied to displayable messages
+ def send_message(self, message):
+ text = MessageController.display_message(message)
+ return text
diff --git a/static/images/bin.png b/static/images/bin.png
new file mode 100644
index 0000000..6126546
Binary files /dev/null and b/static/images/bin.png differ
diff --git a/static/javascript/dashboard.js b/static/javascript/dashboard.js
new file mode 100644
index 0000000..89b51e4
--- /dev/null
+++ b/static/javascript/dashboard.js
@@ -0,0 +1,53 @@
+/* globals Chart:false, feather:false */
+
+(function () {
+ 'use strict'
+
+ feather.replace()
+
+ // Graphs
+ var ctx = document.getElementById('myChart')
+ // eslint-disable-next-line no-unused-vars
+ var myChart = new Chart(ctx, {
+ type: 'line',
+ data: {
+ labels: [
+ 'Sunday',
+ 'Monday',
+ 'Tuesday',
+ 'Wednesday',
+ 'Thursday',
+ 'Friday',
+ 'Saturday'
+ ],
+ datasets: [{
+ data: [
+ 1,
+ 2,
+ 0,
+ 0,
+ 3,
+ 0,
+ 5
+ ],
+ lineTension: 0,
+ backgroundColor: 'transparent',
+ borderColor: '#007bff',
+ borderWidth: 4,
+ pointBackgroundColor: '#007bff'
+ }]
+ },
+ options: {
+ scales: {
+ yAxes: [{
+ ticks: {
+ beginAtZero: false
+ }
+ }]
+ },
+ legend: {
+ display: false
+ }
+ }
+ })
+})()
diff --git a/static/styles/dashboard.css b/static/styles/dashboard.css
new file mode 100644
index 0000000..8b0fa72
--- /dev/null
+++ b/static/styles/dashboard.css
@@ -0,0 +1,100 @@
+body {
+ font-size: .875rem;
+}
+
+.feather {
+ width: 16px;
+ height: 16px;
+ vertical-align: text-bottom;
+}
+
+/*
+ * Sidebar
+ */
+
+.sidebar {
+ position: fixed;
+ top: 0;
+ /* rtl:raw:
+ right: 0;
+ */
+ bottom: 0;
+ /* rtl:remove */
+ left: 0;
+ z-index: 100; /* Behind the navbar */
+ padding: 48px 0 0; /* Height of navbar */
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
+}
+
+@media (max-width: 767.98px) {
+ .sidebar {
+ top: 5rem;
+ }
+}
+
+.sidebar-sticky {
+ position: relative;
+ top: 0;
+ height: calc(100vh - 48px);
+ padding-top: .5rem;
+ overflow-x: hidden;
+ overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
+}
+
+.sidebar .nav-link {
+ font-weight: 500;
+ color: #333;
+}
+
+.sidebar .nav-link .feather {
+ margin-right: 4px;
+ color: #727272;
+}
+
+.sidebar .nav-link.active {
+ color: #007bff;
+}
+
+.sidebar .nav-link:hover .feather,
+.sidebar .nav-link.active .feather {
+ color: inherit;
+}
+
+.sidebar-heading {
+ font-size: .75rem;
+ text-transform: uppercase;
+}
+
+/*
+ * Navbar
+ */
+
+.navbar-brand {
+ padding-top: .75rem;
+ padding-bottom: .75rem;
+ font-size: 1rem;
+ background-color: rgba(0, 0, 0, .25);
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
+}
+
+.navbar .navbar-toggler {
+ top: .25rem;
+ right: 1rem;
+}
+
+.navbar .form-control {
+ padding: .75rem 1rem;
+ border-width: 0;
+ border-radius: 0;
+}
+
+.form-control-dark {
+ color: #fff;
+ background-color: rgba(255, 255, 255, .1);
+ border-color: rgba(255, 255, 255, .1);
+}
+
+.form-control-dark:focus {
+ border-color: transparent;
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
+}
diff --git a/static/styles/signin.css b/static/styles/signin.css
new file mode 100644
index 0000000..4732d1f
--- /dev/null
+++ b/static/styles/signin.css
@@ -0,0 +1,39 @@
+html,
+body {
+ height: 100%;
+}
+
+body {
+ display: flex;
+ align-items: center;
+ padding-top: 40px;
+ padding-bottom: 40px;
+ background-color: #f5f5f5;
+}
+
+.form-signin {
+ width: 100%;
+ max-width: 330px;
+ padding: 15px;
+ margin: auto;
+}
+
+.form-signin .checkbox {
+ font-weight: 400;
+}
+
+.form-signin .form-floating:focus-within {
+ z-index: 2;
+}
+
+.form-signin input[type="email"] {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
diff --git a/templates/dashboard.html b/templates/dashboard.html
new file mode 100644
index 0000000..ac6e70f
--- /dev/null
+++ b/templates/dashboard.html
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+ Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Latest Reports
+
+
+
+
+ #
+ Type
+ Location
+ Time
+ Action
+
+
+
+ {% for row in rows %}
+ {% if row[7] == 1 %}
+
+ {{row[0]}}
+ {{row[2]}}
+ {{row[4]}}
+ {{row[5]}}
+
+ See report
+
+
+ {% else %}
+
+ {{row[0]}}
+ {{row[2]}}
+ {{row[4]}}
+ {{row[5]}}
+
+ See report
+
+
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..d125fa0
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,113 @@
+
+
+
+
+ Demo
+
+
+
+
+
+
+
+
+
+
The latest government advice on Coronavirus is now available here
+
+
+
+
+
+
+
Waste and recycling
+
Find Bin Collection Day
+
Order bins
+
Request Garden Services
+
+
+
+
+
+
+
+
Planning and Building
+
Apply for Permission
+
Building Control
+
Search for Applications
+
+
+
Your Council
+
Jobs
+
Local Plan
+
Strategies and Plans
+
Your Councillor
+
Pay the Council
+
+
+
+
+
+
+
+
+
+
+
+
Green Energy
+
For Everyone
+
+
+
+
+ We have been providing our citizens with affordable and reliable green energy solutions since our first day.
+ With the new "Go Green" scheme, everyone can now get green energy.
+
+
How Do I Apply
+
+
+
+
+ Green Energy is your choice for a better world, a green world. Contact us now!
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/report.html b/templates/report.html
new file mode 100644
index 0000000..0494077
--- /dev/null
+++ b/templates/report.html
@@ -0,0 +1,102 @@
+
+
+
+
+ Demo
+
+
+
+
+
+
+
+
+
+
The latest government advice on Coronavirus is now available here
+
+
+
Report a council related issue
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/report_details.html b/templates/report_details.html
new file mode 100644
index 0000000..f13f65f
--- /dev/null
+++ b/templates/report_details.html
@@ -0,0 +1,126 @@
+
+
+
+
+ Demo
+
+
+
+
+
+
+
+
+
+
The latest government advice on Coronavirus is now available here
+
+
+
Issue Details
+
+
+
+
{{title}}
+
{{query}}
+
+
Description:
+
{{description}}
+
+
Location:
+
{{location}}
+
+
Time
+
{{date}}
+
+
+ {% if solved == 1 %}
+
+
+ Archive Issue
+
+
+ {% else %}
+
+
+ Mark as Solved
+
+
+
+
+
+ Email User
+
+
+
+
Past Messages
+
{{messages}}
+
+
+
For any enquiries please contact {{manager_info}}
+
+
+
+
+ {% endif %}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/signin.html b/templates/signin.html
new file mode 100644
index 0000000..8213ce7
--- /dev/null
+++ b/templates/signin.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+ Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Local Council
+ Please sign in
+
+
+
+ Email address
+
+
+
+ Password
+
+
+
+
+ Remember me
+
+
+ Sign in
+ © 2017–2021
+
+
+
+
+
+
+
diff --git a/user.py b/user.py
new file mode 100644
index 0000000..985fa7b
--- /dev/null
+++ b/user.py
@@ -0,0 +1,30 @@
+""" This is the entity, Staff"""
+from user_profile import Profile
+
+
+class User (Profile):
+
+ def __init__(self): # a method to create objects
+ self.__email = ""
+ self.__role = "user"
+ self.__phone = ""
+
+ def get_email(self): # get method
+ return self.__email
+
+ def set_email(self, mail): # set method
+ self.__email = mail
+
+ # part of GoF: Factory
+ def get_phone(self):
+ return self.__phone
+
+ def set_phone(self, phone):
+ self.__phone = phone
+ # -----------------------
+
+ def get_role(self): # get method
+ return self.__role
+
+ def set_role(self, role): # set method
+ self.__role = role
diff --git a/user_profile.py b/user_profile.py
new file mode 100644
index 0000000..789f6a4
--- /dev/null
+++ b/user_profile.py
@@ -0,0 +1,21 @@
+""" This is the interface, Profile"""
+
+
+class Profile:
+
+ def __init__(self): # a method to create objects
+ self._email = ""
+ self._role = ""
+
+ def get_email(self): # get method
+ pass
+
+ def set_email(self, email): # set method
+ pass
+
+ def get_role(self): # get method
+ pass
+
+ def set_role(self, role): # set method
+ pass
+
diff --git a/userfactory.py b/userfactory.py
new file mode 100644
index 0000000..e6f9108
--- /dev/null
+++ b/userfactory.py
@@ -0,0 +1,20 @@
+""" ProfileFacotry"""
+from user import User
+from staff import Staff
+
+
+class UserFactory:
+ # FACTORY class
+ # Group of Four: FACTORY METHOD
+
+ @staticmethod
+ def factory(role):
+ if role is not None:
+ if role.lower() == "staff":
+ return Staff()
+ elif role.lower() == "user":
+ return User()
+ else:
+ return None
+
+