Skip to content
Open

Ren #36

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
4c67c74
Title change
sevensrig Dec 2, 2024
0003d1a
user log in buttons
zootsuitproductions Dec 2, 2024
15ef2a0
Seb CoOp Searcher Button implemented
sevensrig Dec 2, 2024
1236c63
Home page for CoOpSearcher
sevensrig Dec 2, 2024
e48474a
CoOp Searcher subpages created
sevensrig Dec 2, 2024
3605c46
CoOp Searchers subpages created
sevensrig Dec 2, 2024
b326e99
SQL file for database/schema creation
sevensrig Dec 5, 2024
d8a0cce
update subpages for admin
R-Yin-217 Dec 5, 2024
4c53adb
create admi
R-Yin-217 Dec 5, 2024
be49dcb
Merge pull request #1 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
0039ba1
update Home.py
R-Yin-217 Dec 5, 2024
f7b415e
update admin
R-Yin-217 Dec 5, 2024
7f34403
Merge pull request #2 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
e398f5f
update nav.py
R-Yin-217 Dec 5, 2024
a83c6aa
Merge pull request #3 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
592f0ff
update
R-Yin-217 Dec 5, 2024
da816d3
Merge pull request #4 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
19c68c1
add database with fake data
R-Yin-217 Dec 5, 2024
a112143
Merge pull request #5 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
1bd85c0
Example user information inserted
sevensrig Dec 5, 2024
801c745
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 5, 2024
e56f906
Rename files in database-files
sevensrig Dec 5, 2024
b6d15ce
Riley Reviewer My Reviews page with route to api working
zootsuitproductions Dec 5, 2024
9f61572
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
zootsuitproductions Dec 5, 2024
8e4536b
Changed name of products API route to reviews
zootsuitproductions Dec 5, 2024
c8719b9
update admin subpage
R-Yin-217 Dec 5, 2024
e8db62a
Merge pull request #6 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
ec7e3b2
conncet the database CoopPlatform
R-Yin-217 Dec 5, 2024
84760f3
Merge pull request #7 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
ee2a6e8
update database
R-Yin-217 Dec 5, 2024
98d4188
Merge pull request #8 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
9f083c5
merge main
sevensrig Dec 5, 2024
0f43406
add feedback status
R-Yin-217 Dec 5, 2024
b2f8bef
Merge pull request #9 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
ea448c8
removed .env from git ignore
zootsuitproductions Dec 5, 2024
52a2e8b
update feedback for admin
R-Yin-217 Dec 5, 2024
d7d051a
Merge pull request #10 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
9354611
complete feedback for admin
R-Yin-217 Dec 5, 2024
e97a4e8
Merge pull request #11 from zootsuitproductions/ren
R-Yin-217 Dec 5, 2024
ba9ca63
update database
R-Yin-217 Dec 6, 2024
47df4c6
Merge pull request #12 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
05a0485
Can write co op reviews now and it adds them to database
zootsuitproductions Dec 6, 2024
45799c3
update route company
R-Yin-217 Dec 6, 2024
19d58f2
Merge pull request #13 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
01c060e
update company for admin
R-Yin-217 Dec 6, 2024
38e1218
Merge pull request #14 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
479a53b
update company for admin
R-Yin-217 Dec 6, 2024
1a7805c
Merge pull request #15 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
25f8ef7
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
74df423
searcher routes updated
sevensrig Dec 6, 2024
5663a65
Got rid of interwiew reports
sevensrig Dec 6, 2024
33c66d3
Reviews display comments under them. Also added ability to edit reviews
zootsuitproductions Dec 6, 2024
47f85de
Debugging http error
sevensrig Dec 6, 2024
5371c75
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
0654d63
Feedback routes
sevensrig Dec 6, 2024
9d39c65
Fixed merge conflict
zootsuitproductions Dec 6, 2024
3f0d81e
Company reviews page finished
sevensrig Dec 6, 2024
52173a2
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
c75fca9
Skill matching page finished
sevensrig Dec 6, 2024
76f5528
can post reviews for specific company positions
zootsuitproductions Dec 6, 2024
86866f8
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
zootsuitproductions Dec 6, 2024
0924d0c
fix
zootsuitproductions Dec 6, 2024
fed561e
my reviews shows the role and company name!
zootsuitproductions Dec 6, 2024
c67e8ac
Interview Review works
sevensrig Dec 6, 2024
73a4bc2
added interview reports to the reviews dummy data
zootsuitproductions Dec 6, 2024
91e4ad6
Enter Interview report page updated
sevensrig Dec 6, 2024
743c6ee
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
7ee1edd
add company for admin
R-Yin-217 Dec 6, 2024
28716ec
Merge branch 'main' into ren
R-Yin-217 Dec 6, 2024
0b95f01
Merge pull request #16 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
94ef1ec
update database
R-Yin-217 Dec 6, 2024
354788e
Merge pull request #17 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
5efb094
Update 3004_User_Feedback.py
R-Yin-217 Dec 6, 2024
1144fd7
update database
R-Yin-217 Dec 6, 2024
6db0fd0
Merge pull request #18 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
b3ba705
Writing and viewing interview reports is now possible with dummy data…
sevensrig Dec 6, 2024
d56d4ba
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
153b838
update flagged post manegment for admin
R-Yin-217 Dec 6, 2024
5599ded
Merge pull request #19 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
a083290
update admin dashboard
R-Yin-217 Dec 6, 2024
c1f55d4
Merge pull request #20 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
96d0745
update analytics for admin
R-Yin-217 Dec 6, 2024
89cb10c
Merge pull request #21 from zootsuitproductions/ren
R-Yin-217 Dec 6, 2024
a181d12
Spelling mistake
sevensrig Dec 6, 2024
fe2ffdd
editing reviews works, also updated the review display. added .env to…
zootsuitproductions Dec 6, 2024
73fce31
updated display for reviews
zootsuitproductions Dec 6, 2024
16a03a3
added nav bar links for searcher and reviewer
zootsuitproductions Dec 6, 2024
ac0816a
Changed comments in DB to cascade delete to add Ability to delete rev…
zootsuitproductions Dec 6, 2024
3aebe2b
Changed front-end look of searcher pages
sevensrig Dec 6, 2024
303b9f4
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
ec974ec
Got rid of duplicate skills in the skills dropdown
sevensrig Dec 6, 2024
b1f8c6a
Add files via upload
diva-chhabra Dec 6, 2024
5643003
Delete unnecessary files
sevensrig Dec 6, 2024
771433a
Added annalise pages
zootsuitproductions Dec 6, 2024
9a63d33
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
sevensrig Dec 6, 2024
de8487f
Updated readme
sevensrig Dec 6, 2024
594f297
Sebastian page view is a lot cleaner and can post comments.
zootsuitproductions Dec 6, 2024
2591714
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
zootsuitproductions Dec 6, 2024
2d13a84
Added a button to flag reviews and a route corresponding
sevensrig Dec 7, 2024
c054173
update database
R-Yin-217 Dec 7, 2024
8939803
Merge pull request #22 from zootsuitproductions/ren
R-Yin-217 Dec 7, 2024
0d2d5c4
fix copy
R-Yin-217 Dec 7, 2024
2e5fd0d
Merge pull request #23 from zootsuitproductions/ren
R-Yin-217 Dec 7, 2024
d1d7c6f
changes
christinafu-neu Dec 7, 2024
05c6f8f
som changes
christinafu-neu Dec 7, 2024
be478bc
changes
christinafu-neu Dec 7, 2024
d1f9a3b
fixed analyst side bar
zootsuitproductions Dec 7, 2024
05e2f3f
Merge branch 'main' of https://github.com/zootsuitproductions/TRACE-F…
zootsuitproductions Dec 7, 2024
359d217
fixed about page
zootsuitproductions Dec 7, 2024
6351615
deleted project template files
zootsuitproductions Dec 7, 2024
d032601
update companies
R-Yin-217 Dec 7, 2024
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
117 changes: 63 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,78 +1,87 @@
# Fall 2024 CS 3200 Project Template Repository
# COUPE: TRACE for Co-Ops

This repo is a template for your semester project. It includes most of the infrastructure setup (containers) and sample code and data throughout. Explore it fully and ask questions.
COUPE is a data-driven platform designed to revolutionize the co-op search process for Northeastern University students by offering peer-to-peer insights into company experiences. Our mission is to provide students with transparent, verified, and meaningful reviews on workplace culture, roles, and interview processes to help them find their ideal co-op match.

## Prerequisites
## Key Features
- **Peer-Reviewed Insights**
Access authentic reviews about company culture, roles, and work-life balance written by fellow students.
- **Application and Interview Guidance**
Gain detailed insights into application processes, interview questions, and preparation tips.
- **Incentivized Contribution System**
Share your own co-op experiences to unlock access to additional content and earn platform rewards.

- A GitHub Account
- A terminal-based or GUI git client
- VSCode with the Python Plugin
- A distrobution of Python running on your laptop (Choco (for Windows), brew (for Macs), miniconda, Anaconda, etc).
---

## Current Project Components
## Target Users
### **Persona 1: Sebastian Studentson**
A second-year CS student seeking guidance for his first co-op, overwhelmed by options and in need of peer insights to confidently navigate his search.

Currently, there are three major components which will each run in their own Docker Containers:
### **Persona 2: Riley Reviewer**
A fourth-year marketing major who wants to share detailed feedback on their co-op experience to help others make informed decisions.

- Streamlit App in the `./app` directory
- Flask REST api in the `./api` directory
- SQL files for your data model and data base in the `./database-files` directory
### **Persona 3: Alex Admin**
The platform administrator responsible for moderating reviews, resolving disputes, and maintaining the integrity of the platform.

## Suggestion for Learning the Project Code Base
### **Persona 4: Annalise Analyst**
A data analyst evaluating user trends and engagement to drive improvements in platform features and user experience.

If you are not familiar with web app development, this code base might be confusing. You will probably want two versions though:
1. One version for you to explore, try things, break things, etc. We'll call this your **Personal Repo**
1. One version of the repo that your team will share. We'll call this the **Team Repo**.
---

## Technology Stack
- **Frontend:** Streamlit
- **Middleware:** Python Flask
- **Backend:** MySQL
- **Containerization:** Docker
- **Development Tools:** VSCode, DataGrip

### Setting Up Your Personal Repo
---

1. In GitHub, click the **fork** button in the upper right corner of the repo screen.
1. When prompted, give the new repo a unique name, perhaps including your last name and the word 'personal'.
1. Once the fork has been created, clone YOUR forked version of the repo to your computer.
1. Set up the `.env` file in the `api` folder based on the `.env.template` file.
1. Start the docker containers.
## Database Schema
The database consists of multiple interconnected tables, including:
- **User:** Stores user information, including roles (e.g., Admin, Analyst).
- **Companies:** Lists companies with associated industries and locations.
- **Role:** Details job roles, required skills, and associated companies.
- **Reviews:** Houses peer reviews with fields for headings, content, and engagement metrics.
- **Comments:** Allows threaded discussions on reviews.
- **Badges:** Tracks user achievements and contributions.

### Setting Up Your Team Repo
For a complete SQL DDL script, refer to [schema.sql](#).

Before you start: As a team, one person needs to assume the role of *Team Project Repo Owner*.
---

1. The Team Project Repo Owner needs to fork this template repo into their own GitHub account **and give the repo a name consistent with your project's name**. If you're worried that the repo is public, don't. Every team is doing a different project.
1. In the newly forked team repo, the Team Project Repo Owner should go to the **Settings** tab, choose **Collaborators and Teams** on the left-side panel. Add each of your team members to the repository with Write access.
1. Each of the other team members will receive an invitation to join. Obviously accept the invite.
1. Once that process is complete, each team member, including the repo owner, should clone the Team's Repo to their local machines (in a different location than your Personal Project Repo).
## REST API Endpoints
Here is an example of the REST API matrix. For detailed documentation, refer to the `API_DOCS.md` in this repository.

## Controlling the Containers
| Resource | GET | POST | PUT | DELETE |
|----------------------|-------------------------------------------|-------------------------------|---------------------|-------------------|
| `/companiesWithReviews` | Return all companies with reviews. | N/A | N/A | N/A |
| `/reviews` | Retrieve reviews based on filters. | Submit a new review. | Update an existing review. | Delete a review. |
| `/admin/flaggedContent` | Retrieve flagged reviews for moderation. | N/A | Resolve flagged content. | Remove flagged content. |

- `docker compose up -d` to start all the containers in the background
- `docker compose down` to shutdown and delete the containers
- `docker compose up db -d` only start the database container (replace db with the other services as needed)
- `docker compose stop` to "turn off" the containers but not delete them.
---

## ✨ User Stories
### Sebastian Studentson
- As a co-op searcher, I want to find detailed insights about a company’s culture and values to ensure alignment with my work style.

## Handling User Role Access and Control
### Riley Reviewer
- As a former co-op student, I want an easy-to-use feedback submission form to share structured and meaningful reviews.

In most applications, when a user logs in, they assume a particular role. For instance, when one logs in to a stock price prediction app, they may be a single investor, a portfolio manager, or a corporate executive (of a publicly traded company). Each of those *roles* will likely present some similar features as well as some different features when compared to the other roles. So, how do you accomplish this in Streamlit? This is sometimes called Role-based Access Control, or **RBAC** for short.
### Alex Admin
- As an admin, I need to moderate flagged reviews efficiently to maintain the platform's integrity.

The code in this project demonstrates how to implement a simple RBAC system in Streamlit but without actually using user authentication (usernames and passwords). The Streamlit pages from the original template repo are split up among 3 roles - Political Strategist, USAID Worker, and a System Administrator role (this is used for any sort of system tasks such as re-training ML model, etc.). It also demonstrates how to deploy an ML model.
### Annalise Analyst
- As an analyst, I need tools to identify gaps in content, such as underrepresented industries, and track user engagement.

Wrapping your head around this will take a little time and exploration of this code base. Some highlights are below.
---

### Getting Started with the RBAC
1. We need to turn off the standard panel of links on the left side of the Streamlit app. This is done through the `app/src/.streamlit/config.toml` file. So check that out. We are turning it off so we can control directly what links are shown.
1. Then I created a new python module in `app/src/modules/nav.py`. When you look at the file, you will se that there are functions for basically each page of the application. The `st.sidebar.page_link(...)` adds a single link to the sidebar. We have a separate function for each page so that we can organize the links/pages by role.
1. Next, check out the `app/src/Home.py` file. Notice that there are 3 buttons added to the page and when one is clicked, it redirects via `st.switch_page(...)` to that Roles Home page in `app/src/pages`. But before the redirect, I set a few different variables in the Streamlit `session_state` object to track role, first name of the user, and that the user is now authenticated.
1. Notice near the top of `app/src/Home.py` and all other pages, there is a call to `SideBarLinks(...)` from the `app/src/nav.py` module. This is the function that will use the role set in `session_state` to determine what links to show the user in the sidebar.
1. The pages are organized by Role. Pages that start with a `0` are related to the *Political Strategist* role. Pages that start with a `1` are related to the *USAID worker* role. And, pages that start with a `2` are related to The *System Administrator* role.
## Wireframes
Wireframes for primary features, including the company culture overview and feedback submission forms, can be found in the `wireframes` directory.

---

## Deploying An ML Model (Totally Optional for CS3200 Project)

*Note*: This project only contains the infrastructure for a hypothetical ML model.

1. Build, train, and test your ML model in a Jupyter Notebook.
1. Once you're happy with the model's performance, convert your Jupyter Notebook code for the ML model to a pure python script. You can include the `training` and `testing` functionality as well as the `prediction` functionality. You may or may not need to include data cleaning, though.
1. Check out the `api/backend/ml_models` module. In this folder, I've put a sample (read *fake*) ML model in `model01.py`. The `predict` function will be called by the Flask REST API to perform '*real-time*' prediction based on model parameter values that are stored in the database. **Important**: you would never want to hard code the model parameter weights directly in the prediction function. tl;dr - take some time to look over the code in `model01.py`.
1. The prediction route for the REST API is in `api/backend/customers/customer_routes.py`. Basically, it accepts two URL parameters and passes them to the `prediction` function in the `ml_models` module. The `prediction` route/function packages up the value(s) it receives from the model's `predict` function and send its back to Streamlit as JSON.
1. Back in streamlit, check out `app/src/pages/11_Prediction.py`. Here, I create two numeric input fields. When the button is pressed, it makes a request to the REST API URL `/c/prediction/.../...` function and passes the values from the two inputs as URL parameters. It gets back the results from the route and displays them. Nothing fancy here.


## How to Run Locally
1. Clone this repository:
```bash
git clone https://github.com/zootsuitproductions/coupe.git
cd coupe
4 changes: 2 additions & 2 deletions api/.env.template → api/.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ SECRET_KEY=someCrazyS3cR3T!Key.!
DB_USER=root
DB_HOST=db
DB_PORT=3306
DB_NAME=northwind
MYSQL_ROOT_PASSWORD=<put a good password here>
DB_NAME=CoopPlatform
MYSQL_ROOT_PASSWORD=CoopPCrazyS
166 changes: 166 additions & 0 deletions api/backend/companies/companies_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
########################################################
# Companies blueprint of endpoints
########################################################
from flask import Blueprint
from flask import request
from flask import jsonify
from flask import make_response
from flask import current_app
from backend.db_connection import db
from backend.ml_models.model01 import predict

#------------------------------------------------------------
# Create a new Blueprint object, which is a collection of
# routes.
companies = Blueprint('companies', __name__)


#------------------------------------------------------------
# Get all feedback from the system
@companies.route('/companies', methods=['GET'])
def get_companies():
try:
with db.get_db().cursor() as cursor:
cursor.execute('''
SELECT C.companyID, C.name, C.description, C.updatedAT, I.name, L.address, L.city, L.state_province, L.country, R.roleName, R.description, R.skillsRequired, L.locationID AS `Location ID`, I.IndustryID AS `Industry ID`, R.roleID AS `Role ID`
FROM Companies C JOIN Location L
ON C.companyID = L.companyID JOIN CompanyIndustry CI
ON CI.companyID = C.companyID JOIN Industries I
ON I.industryID = CI.industryID JOIN Role R
ON R.companyID = C.companyID
ORDER BY C.companyID, C.name ASC;
''')
theData = cursor.fetchall()

the_response = make_response(jsonify(theData))
the_response.status_code = 200
return the_response

except Exception as e:
current_app.logger.error(f"Error fetching companies: {e}")
return {"error": "An error occurred while fetching companies"}, 500


@companies.route('/companies/companies', methods=['PUT'])
def update_company():
current_app.logger.info('PUT /companies route')
try:
companies_info = request.json

if 'companyID' not in companies_info or 'Company Description' not in companies_info:
return {'error': 'Missing companyID or Company Description'}, 400

company_id = companies_info['companyID']
company_discription = companies_info['Company Description']


query = '''
UPDATE Companies
SET description = %s
WHERE companyID = %s
'''

data = (company_discription, company_id)

cursor = db.get_db().cursor()
cursor.execute(query, data)
db.get_db().commit()

return {'message': 'Companies updated successfully'}, 200

except KeyError as e:
current_app.logger.error(f"Missing key in request JSON: {str(e)}")
return {'error': f'Missing key: {str(e)}'}, 400
except Exception as e:
current_app.logger.error(f"Error updating feedback: {str(e)}")
return {'error': 'An error occurred while updating companies'}, 500
finally:
if 'cursor' in locals() and cursor:
cursor.close()


@companies.route('/companies/roles', methods=['PUT'])
def update_roles():
current_app.logger.info('PUT /feedback route')
try:
companies_info = request.json

if 'RoleID' not in companies_info or 'Skills Required' not in companies_info or 'Role Description' not in companies_info:
return {'error': 'Missing RoleID or Skills Required or Role Description'}, 400

company_id = companies_info['companyID']
role_id = companies_info['RoleID']
skill = companies_info['Skills Required']
role_description = companies_info['Role Description']

query = '''
UPDATE Role
SET description = %s,
skillsRequired = %s
WHERE companyID = %s AND roleID = %s;
'''

data = (role_description, skill, company_id, role_id)

cursor = db.get_db().cursor()
cursor.execute(query, data)
db.get_db().commit()

return {'message': 'Roles updated successfully'}, 200

except KeyError as e:
current_app.logger.error(f"Missing key in request JSON: {str(e)}")
return {'error': f'Missing key: {str(e)}'}, 400
except Exception as e:
current_app.logger.error(f"Error updating roles: {str(e)}")
return {'error': 'An error occurred while updating roles'}, 500
finally:
if 'cursor' in locals() and cursor:
cursor.close()

@companies.route('/industries', methods=['GET'])
def get_industries():
try:
with db.get_db().cursor() as cursor:
cursor.execute('''
SELECT count(C.companyID) AS NumCompany, I.name AS Industry
FROM Companies C JOIN CompanyIndustry CI
ON CI.companyID = C.companyID JOIN Industries I
ON I.industryID = CI.industryID
GROUP BY I.industryID
ORDER BY I.industryID
''')
theData = cursor.fetchall()

the_response = make_response(jsonify(theData))
the_response.status_code = 200
return the_response

except Exception as e:
current_app.logger.error(f"Error fetching companies: {e}")
return {"error": "An error occurred while fetching companies"}, 500

@companies.route('/reviews', methods=['GET'])
def get_reviews():
try:
with db.get_db().cursor() as cursor:
cursor.execute('''
SELECT count(R.reviewID) AS NumReviews, I.name AS Industry
FROM Companies C JOIN CompanyIndustry CI
ON CI.companyID = C.companyID JOIN Industries I
ON I.industryID = CI.industryID JOIN Role RO
ON RO.CompanyID = C.CompanyID JOIN Reviews R
ON RO.roleID = R.roleID
GROUP BY I.industryID
ORDER BY I.industryID
''')
theData = cursor.fetchall()

the_response = make_response(jsonify(theData))
the_response.status_code = 200
return the_response

except Exception as e:
current_app.logger.error(f"Error fetching reviews: {e}")
return {"error": "An error occurred while fetching reviews"}, 500

Loading