From 1e7b314f384965ffbdf21f2b178ff975b270feef Mon Sep 17 00:00:00 2001 From: Iulian Ivighenie Date: Tue, 6 Apr 2021 21:39:13 +0100 Subject: [PATCH] first commit --- .idea/.gitignore | 3 + .idea/StaffProfileDemo.iml | 10 + .idea/inspectionProfiles/Project_Default.xml | 14 ++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + Test/__init__.py | 0 Test/app_test.py | 44 ++++ Test/test_issue.py | 32 +++ Test/test_manager.py | 24 ++ Test/test_messageboard.py | 14 ++ Test/test_staff.py | 39 ++++ Test/test_user.py | 35 +++ Test/test_user_profile.py | 36 +++ Test/test_userfactory.py | 17 ++ app.py | 203 +++++++++++++++++ issue.py | 41 ++++ messageboard.py | 12 + staff.py | 46 ++++ static/images/bin.png | Bin 0 -> 10833 bytes static/javascript/dashboard.js | 53 +++++ static/styles/dashboard.css | 100 +++++++++ static/styles/signin.css | 39 ++++ templates/dashboard.html | 209 ++++++++++++++++++ templates/index.html | 113 ++++++++++ templates/report.html | 102 +++++++++ templates/report_details.html | 126 +++++++++++ templates/signin.html | 65 ++++++ user.py | 30 +++ user_profile.py | 21 ++ userfactory.py | 20 ++ 31 files changed, 1469 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/StaffProfileDemo.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 Test/__init__.py create mode 100644 Test/app_test.py create mode 100644 Test/test_issue.py create mode 100644 Test/test_manager.py create mode 100644 Test/test_messageboard.py create mode 100644 Test/test_staff.py create mode 100644 Test/test_user.py create mode 100644 Test/test_user_profile.py create mode 100644 Test/test_userfactory.py create mode 100644 app.py create mode 100644 issue.py create mode 100644 messageboard.py create mode 100644 staff.py create mode 100644 static/images/bin.png create mode 100644 static/javascript/dashboard.js create mode 100644 static/styles/dashboard.css create mode 100644 static/styles/signin.css create mode 100644 templates/dashboard.html create mode 100644 templates/index.html create mode 100644 templates/report.html create mode 100644 templates/report_details.html create mode 100644 templates/signin.html create mode 100644 user.py create mode 100644 user_profile.py create mode 100644 userfactory.py 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 0000000000000000000000000000000000000000..6126546a40d322b2eb9c83eab21539669976a1df GIT binary patch literal 10833 zcmb_?cQl+`_oygIv}hr^(WU6!Xk$o-Ac;;8W*D7}!5~D`=#yv>gdu|HW|S~Uq78yk zLJ&lW&gew_P2TT&zkC09?{D38?{C(cbz$NUnGrO;{pLl4F^KU0%7dsgz&eAJCcE*UJi}`Jr8@B<3mS#Xuy*$M^!R1 z3O^SU3xtKiJr#(ThlKqf7zw0@Hwl}JOjQHvZ4YsCL;xHdVJ@C(0>s7^0e}lsO~72< zK+3=y?C9*G8whta4m31@1iC?#p#mD}09B+4iGYVA!XAM1aQF04L8=M-MOTH?|1&Hp z0Qd_8;ie|=FQF_9?gPMHa7Tc=gn~FkT22O_s4O9^Af+rLcMBjRC9Nnar6?&aD=sCi zBBh`rs|@(}Pk_V?4s}v_sD1n2yhu-K0?r78w~C~szrVkPzpR88940BPtgI|4B_k;# zBTj-4_X+Ss*dxU~eFXoepzY`bfxCDkT)aF1e<<2Jc=;mK1W1zpYY86S1_u8j?CJAw zMUhk{iM02Yl$MZ^^zitzuD_st5Dy*yml*#D?PC()?I`)s(Z|ad4k7J_li=UTB;Eb@ zjQ#+Uq){<~yO1`;-d)=Z;_Kn)iO|zl6Clk}Qj(WfxGg0QQr4E!mX!hh z2UpM22Vw6Car~RN3yJr?apnG7Too|f(H`LiH}Udv|Jw@gJ9{C#e4M?!0buZ-T~`8# znY(yGz5IQy|JkO0ty>Z_~1pbW={eRV;BuOaAKgROE8PDHCB#Zph{f{e<9{%G@j-DhB zf|FbY$M*<6J&TZe@9GWZ43T~2ZqK2F-VF$`wpBxk0*~mF;{#CDMN!FBPpp6vSZ2GwCUlxIlKjI$Ps1zif zKj2(*pLjj1D&WLNP8O!h!b#C&=f3sCX2mxTlP}!uULyM@02zvD60#y^*BZT1v9ZSQ z$wHF$`au9c^;QFhUWmp+j+$QQ(W3hk@WiF~U~00<+xJR;tto(kWm}a|m_@F@$#g%z z3T=6yR;kCDG1(HTK|E+;lCS_Ka%fEdTs_MphBMH4_|GX^&_vBuzrcWQ4}_rfHOd%n zX*>>pI!zMGec>9jgy>O`c4{ABmK*kr0lU0<%to?ASqgfO#AN3^GVObN!LM#llWRr~ zyF4Tc$HZRV>rjdr4A+oDMr4|vJg`~iot|BjffHLkfCV zPw9zOiJ0Ga{pihQu>&>TbFv#PhRfU24_2*KFJg`KvBhMX5Zw~ZroeHxyV!$qtG={C zH-4(OaKA>(YW5_*dkfWNxb?#R)TqQ4HVu@xBoGMBGjJ6h;0XmBDD{mg^d&w~2=$EM z4(J&>>yto1qF1K7wl46eV9gCmqAAI=lG}W1XcoyJiYYUUrqr?Ge+MTg&XbsBA60u=VDCb+vYv zu)*Wk+C7Hms6gms^IT4wFUyNdO!FB7@;N~EY5nOXD_Ey;5WC0os}}8J&CHR4r5SG* zJJ>O%3)--tk;9W+mqlC3fTPSA(41t2gxdJsywV$?*nzeFI3X*z%M&`YvX92zm6%qy zkX~Pxb+*gfg+J!k3Zo8?ZFNB`lc98$ZAo3GbROqI1KJ3lv5~O=`i~3KsHk4*T@k$9 zffiTby^}(We3pTxB;eA5zh51uf)h9IHL~e6A{sftiD=r3h8>I0Z%%z-0@FLFKFHh4 zc_BUDPN}gV62(og5w+UJOwR&w1j6FvZBVe}qG5*@f+1a>erLHh7sZZDiwy+t1KVvErwTU&b2d#zbm z$-L*_M-g>+Ln=U$S7~9|`9ewr!J^dFQOm~iu|f49szav<=U1W~c{1@NR2!Wr@Nt~r z;er@7x=P7Qr+@V^1ek!1cKF0b^jQq+H=ldoNI6cI;%$+i7N%EG<@g%^0|A4Vj~+W42T2C7ZMcLE=FU*8 z#>OhGzUnUu{k~jXOE`j0@+`!W{2iL$$@w&;s*I#$zPTnllrwnNZz(nrM0M~v!{Fc* z6Et*|!}%%HJW98^I;GQ8a}BF$S>!#q!EA^Gp)LTJ;_hOvjA}KqvhvH>3&~m5zs2;@ zY%uf)%_sgL{+YkSFO4*pSQeUI%6jBUhaK3|7e6@AMqP*%TqjK+OuIdwD@-TP(jHmh z=F3Q*cilLpn>Vi9-SRO~#479Onf%JDxBOC5>ZbfL8bC4M&lppe=kO>tr%aFCnrnRB z1}`O*vNUQ7cUzi#GNnmj-C^&7%LDLDXq+qMs0 zrlp@`k{{p0Fd1FJ4g?>h74-mIOmmxWddIV2LgJ2T?|%Byw?Lb)#r)JLc(=}QR$Oe^ za5j>F_+|fGfs+kcn%it`_Qkg%>1t3UZk$?1v!Da{Ij3UP@eapC?f9Ocn;BwXJGb{^ zpPyJILq$qa<$a)Q6_J;pHgwHGFp8bwx@LT;s4z_-7JPuUF8maNjWBa%((eFYRGoJx zcz3fytg}^b!Z8mf(erznnOs6R4ZAcshv_CW-tDU!_C?e>iN9Lo*S9SmFgb95GxvmV z3|Klz2JC!b_-+<od~z zel(@O(=edMJT3p|zyNmmYzKJ!5!0SMxxRLGgSz^#^A$Z#d~o3~QYfq8uwEN@fh@wp zV4N6O9z)J>X$ssq?mM-em3#KKWlWszrTb8xcr862j^`eVL%GTw*tNu#XCGm*~-(d%Fwh!$0Y4Yo*5kDq_l*YX>R%Tv8MB|iq1_&5U4fM4)|Q}c#7^>#$IGk<%JKJ>gVn>M z=?HHEviHi002&bY!X$jU(y=FK+D9!vx;ZBW2;5+z9`&i+beN>HgjIH48mH5A0~ClB zR|%hPv?L6eC12Zkc~Vle9E=P{XLs274sO8D=LW9qq2fL~87NLKEx4~ylnJo*WLAY% zR%+5f%EYD^CldSAlnuTV2oJS2H12(t)*-rIe=m|a;uC@mpGy*()9ePLV)(&UVXEbA z({>O{bf>D5Kg?VgTV|X_=U8%K_8sEL{w*QVFKliYH zfb4?=elqEEID|J}k1oGS3HJGM)w)TZhgGE9En8e*X53Z6w4RK#@926@w@TH^5{ z+8)xkY|Tva*%Ppj!tpB zl$<~ZGP6D44+BGZ>oT>IT{P}32d#Oy`#<`*mmi2osunSxG1RnUcm(l3v@ zdce0)Xk&(er;+{Z;*y+~+Eop~>N<1&uFN{x(SR%>kf8905xBA7e=sQE7Zkiq` zR-mX#ZjpiBav0R{ru?kWgdUM^HL#6VZ~S&xe!3S`&iOkEon0=Kp_U&qWXhUGnPps} z0N6yK>2_U;05_j)*}tftMYEW4?!Pu#V{yqYmAhYY!ocjI;5sSv(~Vbs8$%Bvca&$h|lKK^hJY!x<# z_&BLX-EJ{@OCx(bzTuoR5xb(c5M(mnhM1_%@J|(r0gz5kgd>cU%ey&d*j8 zsTm=#Sr~V8P}6d`(39G-(fN1?@bJ;z2YJlqr<8t}M@ERT zg@H?R&v{95VzM>2>@c!<(frh*P#BNaYf`sOnPv;ksJeHQ=z-*O;Xb6Qx^`VfF0x-$ zjRN%cB{67qA%qAz|8@>+vwneD@qW1z%g<69YS`If(ctoC>FLB+k!E9p@k)iF@ZAkU zD*eGcBC$mOs?W%Gj6^1;^vZyD$1|BT+taLG8&=T!PmIv(_=b_-hwc4O44=Jy^Sv@& zTaU$xaEg(hzB5;DtxsI;S>doto;^6j2X(t49}GGMvSMV=U>j zTlQ(u8b7Tn6#o!~iRCN#^mSD&=OfO!Z@o#YH+0C+`t-ELiqTmCwxf$BN9WWjpPXm* z)@A6ZPYT1Dc{{OSWAvS)XJtj#idR$Rn;TwX7EglW`!-cvcoKk3F~mYIJ*-|y6wgIg zDZkrJN(#d}Z>y&87pHdb>_uWVutw(QH+I~QRyjT>Hbi3d2Y8*I2U@S!iU)R&r9TY0 zWyU{I4L#iyepYU(!-MleJ~4ZF))KE_L@T_Wy&bgjG+jcGt^l^WT1*stod$R|HkSNc z!K!$L4tvRJvj~$qfjbMmHXwJHg}J6b&cG=-S$2EWmyOuuA4K4KcXGCKw*g&hr-xhd zR!OrpY;4`8v19~Cy@Lq$qS^_}2d(Amh=*wfd!NH0fs9~B+Rg^1$bBQ-KlG{IW{ z6_iCIi91v=ue>(4r$r@>Rjl8|hn0>gByXYkFQn;J$f?aM>QC`m%dZ`|UC7%BuGa*U z}Vyd(4HqdUiC-Ic{xw>sMp|HJk|l2gCx^t8RJ-T|A%rO)sw6~xv zSI~c4lcJHoD%)oZalQv`T9Q9uIGD7Bses%fvQoDFhrh|Q^vv^H^WNM-p;-e84eS8F z(_mS%EiLMat28u|^a_@A@V+i9A0DItyxEJ%9Vt&dYS|cKTu2*a9X>7pJvH0It}q zYH$3E=*(Gj-}oFQJ zZ6__q49rkj+;r8F<`p046Uq#bW;-X=JfGVQ^uraqy_KG~WO5YC0Ni}@=Xq}sn&6s80IG0}Bu-j$*Rz33?WUma7uoWApN|jMbm2w@*bRcrKF*HHq zAP_)USfpcMnPlv zDw-};5SueK!7=%Re|KPaiM#K7;nQjZ2H#E3C#Zr}5tyk`c~DrqbVoOEhm1{9`GEnm z1D+tq5a5;%SWLB@Y|?qSqUdj6VUnj;SMTf)o%U^*aC_VrRG#b&H_DT#aG|L-#NkF~ zdM{mur-hZyoz_M9iT=JbgkvXJNl9k!2w|(r{_B3e<3d~Ft;?s6-QNF_Or|5q@dtCx z<_3L_q^T~vjvqOkd#QX7zVVv*&HA|>xKsC>-D5)7?_GjKO{1cRq_PL-O8YU}?Qp;X zi?LN$Y337NXXnBgfjQcxIKsTGaVwi5`*@bXOk1J=4oaUYkwqESs!!0C#p53-7)1 zWx%adc*rH5oC;9>76)fWQUdruBVT5ZImbPgy-EcLdTstf-2hDKhO_I!)JMwDk&X zIwg13;yU%mrN!a^C5tX&ch<4w{Tj=H`zrK2l5f5_kKIR1lQ&N&yFBtzg^FPStNAA> zltf_517~^RgOB7*b2coz6Z2tUPW>ywP-psAxWy0eIm;IrVK-BI|D5zPOKE5=MV92` zk6zORc1|X>>?k6CSdfmBcApM72bO(}tWllsyjjS3;aPl#W=T6i?-9o71HcWZp^Hge z2Kagvf|JH{e?Xp*4?4lrA5qKAD6;#) zk>FyjiFE)Tl?osV*UpCu(x@)}dbpXGN>sA`@ELMPQJ%h+#aZLLl1wrnM>BIWu+h)* z76ZrVjKSs0aAuJ0y*!&|;HCHPS#E6b_z$(r%)zw#PCeRX(5km2L08JKfGE0HvqhS3 zhY5oCWA^cQxS!MW#SGeddKIr+U=ew9U4)4hQSA*CKp(ueM%E7}eQkoWEbo z8%8%GD(uEXJBb~vR`-Q|UUc6Y8SXjHZc&^lyGrOn7t;w2DOl?uyV6XEHam<^Ew;y> zw_Dv=p*{44mbMJ6$mSl-5V>LC#+2_JOiQ9~ZG^ToQSsz_lwQI2T7nO|*3fI{=56`N zko2sU(Mx?-Xs`pmn?q&k8$;hx18L5w`f91`cYLH0Ir}wZM4ZN}Vj|KN*S&UMIlY`+ zF%@g57Q_nOZB-&(?7L8bbUI=`)&WC};U~5}RON+v?G;h5W&UW~o3YbR6k2TA>K@`A zSLeiO*ybeJ+mQ7$3p(-E`J;Z;;6@7gnJw{m;ah_7z#|=+>Mv%7s3?H-Y)6B{?qFE| zH%_4E4YwmvG)#EB+U0)O0YP+!(1Be^kg)oc{NM#o^;M{T!%erBXk(DAc^A5W%+!Xv;JFaGCfs*1WsU`x?l|aIk=5^29nlkYMST3P+Xbi=naybz zg8sSlql>W67M1wC_uYX|c9XD`2>YepX4O#u{^sJ-w`fsp0g2sR@32Th()REt3r=<= z+op2J&7VAc3r$7ut8k#AS0*(#A$Rxuo{tjSz){t-@TKVBhd=jfgcjJ=I5gMl{ckPL z>A~4}Wp|4`Kkm+D_4$NGvRLE- zipp;`5JmQ5YTo^(@lpY5j~l&r@gup~0hVd>?YYF-xnbkxse~F&)GbQOXIt{WA%2t` zGFRu*g{}74taQ-w6mnqVICsB?OZZZe1#YO)?MyJHZl%DEoak%MCsIF6IQzo9+x1cQ zttOlEcZaGgp0_no;>oR6lA7sv*SVej6STR)7g8;xzY-R1!|c+E9<3wKy|UArdso*+ zUUz<7=9(zpU*vgJkB`r2EAxx0;YacPD7GCB40P9?A0!eL4~TwYOlwJ-n&atQ=pYvq zwOE6TAcZ`jdNCt98kut}D`{r~+)g3nDLBdu<}6ep;3g>~=J;`Fmq23^uG+jB%lyqR zYWa5)=2=}Je~(_1-_ns*HYhmVEgVoNgg;Lf6I{WO-ti6lMHu~u>V>Al*-hKqTKbu; z_f8AwG$0lUotw}KAIPc2`tO4}aw9;K6N{5Deq`5$k^^L@k4oKXGG`kXS$2Sp>Nl|1 zfNvss)HBh59OpHb?4Rc#(Bkwrt$Ws|lPfh0fdT!*yT_H^DEd6RtWn}!{3=xkoZ}II zLNR=|cQ{FI-TK>AsWrlPWkQW|wD#q&LosDR#%$2jm%*(X(g{!Y8?<8ty!H=+;_Iq7 zvVxZG6nT^-DLFh6Tbd#Y2cNlz`=86)kLmQd1!}YJsB9(nOR2jWZT#%YWtIGO3m?rfu{^)XBc4@%^M>mCV|^tX z^?GR!vz{r^m7N8|oBjp1^seVI7?_~wmANU4ke!yN@IYit_R!bAADL(9eXZIQwb%mr z={tEr*hHqbvW+p)U5%6+1hEmHSgz2oKKxkk%ZMnPqorGaQ^|!_aL_*^P?rU}BF}<_ zam|wtl{92QBY=~XD_(asQA3}s0(Wv>6C2|O+_ua7`@HHS0$fXP8 z4qStB6*}el#w@OSuL*(tbDO&e5?I<831Q4|Z_%-Bz)?Qb;7kek+32$19L_Q=jJtaT zjLOtYvkZLor59Z_V}HS(0o}9ye)y2{gEL(|*;Vk69_a^}$3?dd67EWeCevc{Vw9-}%t+kr6dhT;ZU6e$g>k7BhkuphIl z_%>HB{2Y4cx>NcpPKofn+;sa+HsNE`I=M$*!tr~ZKs>VRGMp)_)I#eTuxIy|T+QKA zXZkYF(g4GRipeh>d<6|6&)T*UVdIh&YrHaH@qmMl365vc6YXgWCFp3!1jSs_rIN2S zQ3~n$BtZfPjlQgoup~s@q6&yd)03Q{72ezVYf~)>*f>luG%hiJ>Ae*&`QZ2L8@onx zdJj39;Pp2LU@?P+Um*cV{C!h~fJcVY?X&H4t-!O0Yq(24mgkKJ%eav6-D*2qR#j;A9^*X6fJf8zS8wB)`rOt7 z_OaYm|2pjqe}07gZ65G7Fov5}CkWq$Vw%sx&0#$S61b+vE8U7R{L>5^R@oW|zTZk} z0&I-moj)`Kjc~0(C^;Nx;7s2Jc$us(+730%)i2uA5!wl*onLNoLG7jTWO zI9aUGnfmnW}Cm? z!74rPBpt17Y3Rrb)foyAU+%~FC>L&_zOPt2XXcpfa)YO;q8ddCwm_FMwWW7Vf8g(Q*k4wn~IcrSB*6RblAYKQK3ZSK16LJ>C8wfNy)bh3dJ1U)kevzv)^+wE}9#@dhK>0C)kpf*0R`` zw6us2{<(fawtU}*8Ku5k+{rZne`>0zPa$QUsVB$hrtU6ZOaT>q=?&}ZWh`kKWvRr> zR_Y~#+WVK1s_yf3KeE8bzNbF7$*aMZEFu^R+K>Cr*4hteveb86U_rVj@m<{>!mD+~ z%&e~lLxLoF7`h=+GyGu1Q#Syrq+XL#uA?G2@*U&+(KZe<%{~$Gec13uIKVBf&pm#$Bd9a2iu~Jc+C7UA z(Uv#>?9y1^HKx}lFxSCs=V$V~(=R`%xvC*nGw@;q?N|28ACY+$IS2x;>j+<^;Ds+z@!u)rShdI87v6J^jB9*DgqGw zSVK({<#WVsW+}(jGsM6v10Y2i?vpcE)&=L>QzV6I8TgcgM5!ccR<7fbon*>1)y!|c zga1WA^bZ9R1Je5LdMl)#@tj$ZDrS&)RB|*a@t1NBbq)XfSTh6?kD}7-qEyCiwj?pu zI_LS5CXq*Jc(9oSd|x8Hz-$+cy3j3(Ps{YQkxlA8 zo$Rm@U{GQ?8D2xy&BOCjS)2wiC`JH^#SehM;8l4|~E3!na}YU58~)&I}< vKNULtp9-MZ8_u-_G? + + + + + + + + Demo + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+

Dashboard

+
+
+ + +
+ +
+
+ + + +

Latest Reports

+
+ + + + + + + + + + + + {% for row in rows %} + {% if row[7] == 1 %} + + + + + + + + {% else %} + + + + + + + + {% endif %} + + + {% endfor %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#TypeLocationTimeAction
{{row[0]}} {{row[2]}} {{row[4]}}{{row[5]}} + See report +
{{row[0]}} {{row[2]}} {{row[4]}}{{row[5]}} + See report +
+
+
+
+
+ + + + + + + + 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 + + + + + + + +
+
+
+
+
+
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. +

+ + +
+ +
+ + +
+ + \ 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 + + + + + + + +
+

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 + + + + + + + +
+

Issue Details

+

+
+
+

{{title}}

+ {{query}} +
+
Description:
+

{{description}}

+
+
Location:
+

{{location}}

+
+
Time
+

{{date}}

+ +
+ {% if solved == 1 %} +
+
+ +
+
+ {% else %} +
+
+ +
+
+
+
+ +
+
+

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

+ +
+ + +
+
+ + +
+ +
+ +
+ +

© 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 + +