├── .dockerignore ├── .env.example ├── .flaskenv ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── Pipfile ├── Pipfile.lock ├── Programmerr.pdf ├── README.md ├── app ├── __init__.py ├── api │ ├── auth_routes.py │ ├── category_routes.py │ ├── language_routes.py │ ├── service_routes.py │ └── user_routes.py ├── aws.py ├── config.py ├── forms │ ├── __init__.py │ ├── login_form.py │ └── signup_form.py ├── models │ ├── __init__.py │ ├── bug.py │ ├── bug_package.py │ ├── category.py │ ├── db.py │ ├── education.py │ ├── language.py │ ├── req_answer.py │ ├── requirement.py │ ├── review.py │ ├── service.py │ ├── service_language.py │ ├── service_requirement.py │ ├── skill.py │ ├── user.py │ ├── user_language.py │ ├── web.py │ └── web_package.py └── seeds │ ├── __init__.py │ ├── bug_packages.py │ ├── bugs.py │ ├── categories.py │ ├── languages.py │ ├── reviews.py │ ├── service_languages.py │ ├── services.py │ ├── text.py │ ├── users.py │ ├── web_packages.py │ └── webs.py ├── architecture.pdf ├── dev-requirements.txt ├── flask-help.md ├── migrations ├── README ├── alembic.ini ├── env.py ├── script.py.mako └── versions │ └── 20210613_160626_.py ├── react-app ├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── splash.png └── src │ ├── App.js │ ├── components │ ├── AboutMe │ │ ├── AboutMe.css │ │ └── index.js │ ├── CategoryPage │ │ ├── CategoryPage.css │ │ └── index.js │ ├── EditService │ │ ├── Description.css │ │ ├── EditService.css │ │ ├── Pricing.css │ │ ├── Requirements.css │ │ └── index.js │ ├── Footer │ │ ├── Footer.css │ │ └── index.js │ ├── HomePage │ │ ├── HomePage.css │ │ └── index.js │ ├── LoginFormModal │ │ ├── Login.js │ │ ├── LoginFormModal.css │ │ ├── Signup.js │ │ └── index.js │ ├── MultiPackageComponent │ │ ├── MultiPackageComponent.css │ │ └── index.js │ ├── NavBar │ │ ├── NavBar.css │ │ └── index.js │ ├── NewService │ │ ├── NewService.css │ │ └── index.js │ ├── PageNotFound │ │ ├── PageNotFound.css │ │ └── index.js │ ├── ProfilePage │ │ ├── ProfilePage.css │ │ └── index.js │ ├── PublicProfile │ │ ├── PublicProfile.css │ │ └── index.js │ ├── ServiceCard │ │ ├── ServiceCard.css │ │ └── index.js │ ├── ServicePage │ │ ├── ServicePage.css │ │ └── index.js │ ├── SplashPage │ │ ├── SplashPage.css │ │ └── index.js │ ├── User.js │ ├── UserServiceCard │ │ ├── UserServiceCard.css │ │ └── index.js │ ├── UsersList.js │ └── auth │ │ ├── LoginForm.js │ │ ├── LoginSignup.css │ │ ├── LogoutButton.js │ │ ├── ProtectedRoute.js │ │ ├── SignUpForm.js │ │ ├── ex.js │ │ └── lex.js │ ├── context │ ├── Modal.css │ └── Modal.js │ ├── index.css │ ├── index.js │ ├── services │ └── auth.js │ └── store │ ├── category.js │ ├── index.js │ ├── language.js │ ├── service.js │ ├── session.js │ └── user.js ├── redux-steps.md └── requirements.txt /.dockerignore: -------------------------------------------------------------------------------- 1 | react-app/node_modules 2 | .venv 3 | Pipfile 4 | Pipfile.lock 5 | .env 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | FLASK_APP=app 2 | FLASK_ENV=development 3 | SECRET_KEY=lkasjdf09ajsdkfljalsiorj12n3490re9485309irefvn,u90818734902139489230 4 | DATABASE_URL=postgresql://starter_app_dev@localhost/starter_app 5 | -------------------------------------------------------------------------------- /.flaskenv: -------------------------------------------------------------------------------- 1 | FLASK_APP=app -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | __pycache__/ 3 | *.py[cod] 4 | .venv 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "/usr/local/bin/python3", 3 | "python.linting.pylintEnabled": false, 4 | "python.linting.enabled": true, 5 | // "python.linting.pycodestyleEnabled": true, 6 | "editor.detectIndentation": false 7 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 AS build-stage 2 | 3 | WORKDIR /react-app 4 | COPY react-app/. . 5 | 6 | # You have to set this because it should be set during build time. 7 | ENV REACT_APP_BASE_URL=https://programmerr-app.herokuapp.com/ 8 | 9 | # Build our React App 10 | RUN npm install 11 | RUN npm run build 12 | 13 | FROM python:3.8 14 | 15 | # Setup Flask environment 16 | ENV FLASK_APP=app 17 | ENV FLASK_ENV=production 18 | ENV SQLALCHEMY_ECHO=True 19 | 20 | EXPOSE 8000 21 | 22 | WORKDIR /var/www 23 | COPY . . 24 | COPY --from=build-stage /react-app/build/* app/static/ 25 | 26 | # Install Python Dependencies 27 | RUN pip install -r requirements.txt 28 | RUN pip install psycopg2 29 | 30 | # Run flask environment 31 | CMD gunicorn app:app 32 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | click = "==7.1.2" 8 | gunicorn = "==20.0.4" 9 | itsdangerous = "==1.1.0" 10 | python-dotenv = "==0.14.0" 11 | six = "==1.15.0" 12 | Flask = "==1.1.2" 13 | Flask-Cors = "==3.0.8" 14 | Flask-SQLAlchemy = "==2.4.4" 15 | Flask-WTF = "==0.14.3" 16 | Jinja2 = "==2.11.2" 17 | MarkupSafe = "==1.1.1" 18 | SQLAlchemy = "==1.3.19" 19 | Werkzeug = "==1.0.1" 20 | WTForms = "==2.3.3" 21 | Flask-JWT-Extended = "==3.24.1" 22 | email-validator = "==1.1.2" 23 | Flask-Migrate = "==2.5.3" 24 | Flask-Login = "==0.5.0" 25 | alembic = "==1.4.3" 26 | python-dateutil = "==2.8.1" 27 | python-editor = "==1.0.4" 28 | Mako = "==1.1.3" 29 | PyJWT = "==1.7.1" 30 | boto3 = "*" 31 | Faker = "==8.5.1" 32 | Flask-SocketIO = "*" 33 | dnspython = "==2.1.0" 34 | idna = "==3.2" 35 | text-unidecode = "==1.3" 36 | 37 | [dev-packages] 38 | psycopg2-binary = "==2.8.6" 39 | autopep8 = "*" 40 | pylint = "*" 41 | 42 | [requires] 43 | python_version = "3.9" 44 | -------------------------------------------------------------------------------- /Programmerr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixzzels/Programmerr/40c9d563e3a2187d4621c1b58bbd07e06aac5640/Programmerr.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programmerr 2 | 3 | Programmerr, an almost pixel perfect clone by Fiverr, an online marketplace for freelance services. 4 | 5 | #### Live link: [Programmerr](https://programmerr-app.herokuapp.com/) 6 | 7 | *** 8 | 9 | ### Index 10 | [Technologies](#technologies) 11 | 12 | [Key Features](#key-features) 13 | 14 | [Wiki Pages](#wiki-pages) 15 | 16 | [Stretch Goals](#stretch-goals) 17 | 18 | *** 19 | 20 | ### Technologies 21 | #### Front End 22 | - JavaScript 23 | - React and Redux 24 | - CSS styling 25 | - [Favicon.io](https://favicon.io/) for favicon 26 | - Adobe XD 27 | - Hosted on Heroku 28 | 29 | #### Back End 30 | - Uses python flask backend 31 | - SQLAlchemy 32 | 33 | *** 34 | 35 | ### Wiki Pages 36 | #### [Database Schema](https://github.com/pixzzels/programmerr/wiki/Database-Schema) 37 | #### [Feature List](https://github.com/pixzzels/programmerr/wiki/MVP-Feature-List) 38 | #### [Back End Routes](https://github.com/pixzzels/programmerr/wiki/Back-End-Routes) 39 | #### [Front End Routes](https://github.com/pixzzels/programmerr/wiki/Front-End-Routes) 40 | 41 | 42 | *** 43 | 44 | ### Stretch Goals 45 | - Messaging between buyer and seller 46 | - Track service completion status 47 | - Show detailed service stats 48 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | from app.models import category 2 | import os 3 | from flask import Flask, render_template, request, session, redirect 4 | from flask_cors import CORS 5 | from flask_migrate import Migrate 6 | from flask_wtf.csrf import CSRFProtect, generate_csrf 7 | from flask_login import LoginManager 8 | 9 | from .models import db, User 10 | from .api.user_routes import user_routes 11 | from .api.auth_routes import auth_routes 12 | from .api.language_routes import language_routes 13 | from .api.category_routes import category_routes 14 | from .api.service_routes import service_routes 15 | 16 | from .seeds import seed_commands 17 | 18 | from .config import Config 19 | 20 | app = Flask(__name__) 21 | 22 | # Setup login manager 23 | login = LoginManager(app) 24 | login.login_view = 'auth.unauthorized' 25 | 26 | 27 | @login.user_loader 28 | def load_user(id): 29 | return User.query.get(int(id)) 30 | 31 | 32 | # Tell flask about our seed commands 33 | app.cli.add_command(seed_commands) 34 | 35 | app.config.from_object(Config) 36 | app.register_blueprint(user_routes, url_prefix='/api/users') 37 | app.register_blueprint(auth_routes, url_prefix='/api/auth') 38 | app.register_blueprint(language_routes, url_prefix='/api/language') 39 | app.register_blueprint(category_routes, url_prefix='/api/category') 40 | app.register_blueprint(service_routes, url_prefix='/api/service') 41 | 42 | 43 | 44 | db.init_app(app) 45 | Migrate(app, db) 46 | 47 | # Application Security 48 | CORS(app) 49 | 50 | # Since we are deploying with Docker and Flask, 51 | # we won't be using a buildpack when we deploy to Heroku. 52 | # Therefore, we need to make sure that in production any 53 | # request made over http is redirected to https. 54 | # Well......... 55 | 56 | 57 | @app.before_request 58 | def https_redirect(): 59 | if os.environ.get('FLASK_ENV') == 'production': 60 | if request.headers.get('X-Forwarded-Proto') == 'http': 61 | url = request.url.replace('http://', 'https://', 1) 62 | code = 301 63 | return redirect(url, code=code) 64 | 65 | 66 | @app.after_request 67 | def inject_csrf_token(response): 68 | response.set_cookie('csrf_token', 69 | generate_csrf(), 70 | secure=True if os.environ.get( 71 | 'FLASK_ENV') == 'production' else False, 72 | samesite='Strict' if os.environ.get( 73 | 'FLASK_ENV') == 'production' else None, 74 | httponly=True) 75 | return response 76 | 77 | 78 | @app.route('/', defaults={'path': ''}) 79 | @app.route('/') 80 | def react_root(path): 81 | print("path", path) 82 | if path == 'favicon.ico': 83 | return app.send_static_file('favicon.ico') 84 | return app.send_static_file('index.html') 85 | -------------------------------------------------------------------------------- /app/api/auth_routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, session, request 2 | from app.models import User, db 3 | from app.forms import LoginForm 4 | from app.forms import SignUpForm 5 | from flask_login import current_user, login_user, logout_user, login_required 6 | 7 | auth_routes = Blueprint('auth', __name__) 8 | 9 | 10 | def validation_errors_to_error_messages(validation_errors): 11 | """ 12 | Simple function that turns the WTForms validation errors into a simple list 13 | """ 14 | errorMessages = [] 15 | for field in validation_errors: 16 | for error in validation_errors[field]: 17 | errorMessages.append(f"{field} : {error}") 18 | return errorMessages 19 | 20 | 21 | @auth_routes.route('/') 22 | def authenticate(): 23 | """ 24 | Authenticates a user. 25 | """ 26 | if current_user.is_authenticated: 27 | return current_user.to_dict() 28 | return {'errors': ['Unauthorized']} 29 | 30 | 31 | @auth_routes.route('/login', methods=['POST']) 32 | def login(): 33 | """ 34 | Logs a user in 35 | """ 36 | form = LoginForm() 37 | print(request.get_json()) 38 | # Get the csrf_token from the request cookie and put it into the 39 | # form manually to validate_on_submit can be used 40 | form['csrf_token'].data = request.cookies['csrf_token'] 41 | if form.validate_on_submit(): 42 | # Add the user to the session, we are logged in! 43 | user = User.query.filter(User.email == form.data['email']).first() 44 | login_user(user) 45 | return user.to_dict() 46 | return {'errors': validation_errors_to_error_messages(form.errors)}, 401 47 | 48 | 49 | @auth_routes.route('/logout') 50 | def logout(): 51 | """ 52 | Logs a user out 53 | """ 54 | logout_user() 55 | return {'message': 'User logged out'} 56 | 57 | 58 | @auth_routes.route('/signup', methods=['POST']) 59 | def sign_up(): 60 | """ 61 | Creates a new user and logs them in 62 | """ 63 | form = SignUpForm() 64 | form['csrf_token'].data = request.cookies['csrf_token'] 65 | if form.validate_on_submit(): 66 | user = User( 67 | username=form.data['username'], 68 | email=form.data['email'], 69 | password=form.data['password'] 70 | ) 71 | db.session.add(user) 72 | db.session.commit() 73 | login_user(user) 74 | return user.to_dict() 75 | return {'errors': validation_errors_to_error_messages(form.errors)}, 401 76 | 77 | 78 | @auth_routes.route('/unauthorized') 79 | def unauthorized(): 80 | """ 81 | Returns unauthorized JSON when flask-login authentication fails 82 | """ 83 | return {'errors': ['Unauthorized']}, 401 84 | -------------------------------------------------------------------------------- /app/api/category_routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, request 2 | from flask_login import login_required 3 | from app.models import Category 4 | 5 | category_routes = Blueprint('categories', __name__) 6 | 7 | 8 | @category_routes.route('/') 9 | def category(): 10 | categories = Category.query.all() 11 | return jsonify([category.to_dict() for category in categories]) 12 | -------------------------------------------------------------------------------- /app/api/language_routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, request 2 | from flask_login import login_required 3 | from app.models import Language, db 4 | 5 | language_routes = Blueprint('languages', __name__) 6 | 7 | 8 | @language_routes.route('/') 9 | def languages(): 10 | languages = Language.query.all() 11 | return jsonify([language.to_dict() for language in languages]) 12 | -------------------------------------------------------------------------------- /app/api/service_routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, request 2 | from flask_login import current_user, login_required 3 | from app.models import Service, ServiceLanguage, db, Web, WebPackage, User 4 | from app.aws import ( 5 | upload_file_to_s3, allowed_file, get_unique_filename) 6 | 7 | 8 | service_routes = Blueprint('services', __name__) 9 | 10 | 11 | @service_routes.route('/') 12 | def all_services(): 13 | services = Service.query.filter(Service.publish == True).all() 14 | return jsonify([service.to_dict() for service in services]) 15 | 16 | 17 | @service_routes.route('/') 18 | def one_service(id): 19 | """ 20 | Gets a service based on id. 21 | """ 22 | service = Service.query.get(id) 23 | return service.to_dict() 24 | 25 | 26 | @service_routes.route('/programming-lang') 27 | def programming_languages(): 28 | """ 29 | Gets all programming languages in database. 30 | """ 31 | services = ServiceLanguage.query.all() 32 | return jsonify([service.to_dict() for service in services]) 33 | 34 | 35 | @service_routes.route('/add/overview', methods=["POST"]) 36 | def add_new_service_overview(): 37 | """ 38 | Adds a new service 39 | """ 40 | new_service = Service(**request.json) 41 | db.session.add(new_service) 42 | db.session.commit() 43 | return new_service.dict_overview() 44 | 45 | 46 | @service_routes.route('/owned/') 47 | def user_services(id): 48 | """ 49 | Gets all services associated with a user 50 | """ 51 | services = Service.query.filter(Service.user_id == id).all() 52 | 53 | return jsonify([service.to_dict() for service in services]) 54 | 55 | 56 | @service_routes.route('/user/edit/') 57 | def user_service_edit(serviceId): 58 | """ 59 | Gets service to edit 60 | """ 61 | service = Service.query.get(serviceId) 62 | if service.web_package_id: 63 | return service.to_dict() 64 | else: 65 | return service.dict_overview() 66 | 67 | 68 | @service_routes.route('/update/overview/', methods=["PUT"]) 69 | @login_required 70 | def update_service_overview(serviceId): 71 | service = Service.query.get(serviceId) 72 | 73 | service.title = request.json["title"] 74 | service.category_id = request.json["category_id"] 75 | service.service_language_id = request.json["service_language_id"] 76 | 77 | db.session.add(service) 78 | db.session.commit() 79 | 80 | return service.dict_overview() 81 | 82 | 83 | @service_routes.route('/update/basic/', methods=["POST"]) 84 | @login_required 85 | def update_basic_package(serviceId): 86 | 87 | service = Service.query.get(serviceId) 88 | 89 | if service.web_package_id is None: 90 | web = Web(**request.json) 91 | # print("FLAAGs", web.id) 92 | 93 | db.session.add(web) 94 | db.session.commit() 95 | 96 | basicId = (web.id) 97 | 98 | webPackage = WebPackage(web_basic_id=basicId) 99 | db.session.add(webPackage) 100 | db.session.commit() 101 | 102 | webPackageId = webPackage.id 103 | 104 | service.web_package_id = webPackageId 105 | 106 | db.session.commit() 107 | 108 | return service.to_dict() 109 | 110 | webPackage = WebPackage.query.get(service.web_package_id) 111 | webBasic = Web.query.get(webPackage.web_basic_id) 112 | 113 | webBasic.type = request.json["type"] 114 | webBasic.title = request.json["title"] 115 | webBasic.description = request.json["description"] 116 | webBasic.delivery_time = request.json["delivery_time"] 117 | webBasic.pages = request.json["pages"] 118 | webBasic.design_custom = request.json["design_custom"] 119 | webBasic.content_upload = request.json["content_upload"] 120 | webBasic.responsive_design = request.json["responsive_design"] 121 | webBasic.source_code = request.json["source_code"] 122 | webBasic.revisions = request.json["revisions"] 123 | webBasic.price = request.json["price"] 124 | 125 | db.session.add(webBasic) 126 | db.session.commit() 127 | return service.to_dict() 128 | 129 | 130 | @service_routes.route('/update/standard/', methods=["POST"]) 131 | @login_required 132 | def update_standard_package(serviceId): 133 | 134 | service = Service.query.get(serviceId) 135 | webPackage = WebPackage.query.get(service.web_package_id) 136 | 137 | if webPackage.web_standard_id is None: 138 | web = Web(**request.json) 139 | # print("FLAAGs", web.id) 140 | 141 | db.session.add(web) 142 | db.session.commit() 143 | 144 | standardId = (web.id) 145 | 146 | webPackage.web_standard_id = (standardId) 147 | db.session.add(webPackage) 148 | db.session.commit() 149 | 150 | return service.to_dict() 151 | 152 | webStandard = Web.query.get(webPackage.web_standard_id) 153 | 154 | webStandard.type = request.json["type"] 155 | webStandard.title = request.json["title"] 156 | webStandard.description = request.json["description"] 157 | webStandard.delivery_time = request.json["delivery_time"] 158 | webStandard.pages = request.json["pages"] 159 | webStandard.design_custom = request.json["design_custom"] 160 | webStandard.content_upload = request.json["content_upload"] 161 | webStandard.responsive_design = request.json["responsive_design"] 162 | webStandard.source_code = request.json["source_code"] 163 | webStandard.revisions = request.json["revisions"] 164 | webStandard.price = request.json["price"] 165 | 166 | db.session.add(webStandard) 167 | db.session.commit() 168 | return service.to_dict() 169 | 170 | 171 | @service_routes.route('/update/premium/', methods=["POST"]) 172 | @login_required 173 | def update_premium_package(serviceId): 174 | 175 | service = Service.query.get(serviceId) 176 | webPackage = WebPackage.query.get(service.web_package_id) 177 | 178 | if webPackage.web_premium_id is None: 179 | web = Web(**request.json) 180 | # print("FLAAGs", web.id) 181 | 182 | db.session.add(web) 183 | db.session.commit() 184 | 185 | premiumId = (web.id) 186 | 187 | webPackage.web_premium_id = (premiumId) 188 | db.session.add(webPackage) 189 | db.session.commit() 190 | 191 | return service.to_dict() 192 | 193 | webPremium = Web.query.get(webPackage.web_premium_id) 194 | 195 | webPremium.type = request.json["type"] 196 | webPremium.title = request.json["title"] 197 | webPremium.description = request.json["description"] 198 | webPremium.delivery_time = request.json["delivery_time"] 199 | webPremium.pages = request.json["pages"] 200 | webPremium.design_custom = request.json["design_custom"] 201 | webPremium.content_upload = request.json["content_upload"] 202 | webPremium.responsive_design = request.json["responsive_design"] 203 | webPremium.source_code = request.json["source_code"] 204 | webPremium.revisions = request.json["revisions"] 205 | webPremium.price = request.json["price"] 206 | 207 | db.session.add(webPremium) 208 | db.session.commit() 209 | return service.to_dict() 210 | 211 | 212 | @service_routes.route('/update/description/', methods=["PUT"]) 213 | @login_required 214 | def update_service_description(serviceId): 215 | service = Service.query.get(serviceId) 216 | 217 | service.description = request.json["description"] 218 | 219 | db.session.add(service) 220 | db.session.commit() 221 | 222 | return service.dict_overview() 223 | 224 | 225 | @service_routes.route('/publish/', methods=["PUT"]) 226 | @login_required 227 | def update_service_publish(serviceId): 228 | """ 229 | Changes a service publish to be true or false 230 | """ 231 | service = Service.query.get(serviceId) 232 | 233 | service.publish = request.json["publish"] 234 | 235 | db.session.add(service) 236 | db.session.commit() 237 | 238 | return service.dict_overview() 239 | 240 | 241 | @service_routes.route('/delete/', methods=["DELETE"]) 242 | @login_required 243 | def delete_service(serviceId): 244 | """ 245 | Deletes a service 246 | """ 247 | service = Service.query.get(serviceId) 248 | 249 | db.session.delete(service) 250 | db.session.commit() 251 | return service.dict_overview() 252 | 253 | 254 | ## IMAGE ROUTES 255 | 256 | @service_routes.route("update/image/", methods=["POST"]) 257 | @login_required 258 | def upload_image(serviceId): 259 | # print('request!!', request.files) 260 | # print("test1") 261 | if "image" not in request.files: 262 | return {"errors": "image required"}, 400 263 | # print("test2") 264 | 265 | image = request.files["image"] 266 | 267 | # print('image', image) 268 | 269 | if not allowed_file(image.filename): 270 | return {"errors": "file type not permitted"}, 400 271 | 272 | # print("test3") 273 | 274 | 275 | image.filename = get_unique_filename(image.filename) 276 | 277 | upload = upload_file_to_s3(image) 278 | 279 | # print("upload", upload) 280 | 281 | if "url" not in upload: 282 | # if the dictionary doesn't have a url key 283 | # it means that there was an error when we tried to upload 284 | # so we send back that error message 285 | return upload, 400 286 | 287 | url = upload["url"] 288 | # flask_login allows us to get the current user from the request 289 | # current_use = User.query.get(current_user) 290 | service = Service.query.get(serviceId) 291 | service.listing_img = url 292 | # new_image = Service(user=current_user, url=url) 293 | db.session.add(service) 294 | db.session.commit() 295 | return service.to_dict() -------------------------------------------------------------------------------- /app/api/user_routes.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify, request 2 | from flask_login import login_required 3 | from app.models import User, UserLanguage, Skill, db 4 | 5 | user_routes = Blueprint('users', __name__) 6 | 7 | 8 | @user_routes.route('/') 9 | @login_required 10 | def users(): 11 | users = User.query.all() 12 | return {"users": [user.to_dict() for user in users]} 13 | 14 | 15 | @user_routes.route('/') 16 | @login_required 17 | def user(id): 18 | users = User.query.get(id) 19 | return users.to_dict() 20 | 21 | @user_routes.route('/public/') 22 | def user_public(username): 23 | users = User.query.filter(User.username == username).all() 24 | return jsonify([user.to_dict() for user in users]) 25 | 26 | 27 | @user_routes.route('/tagline/', methods=["PUT"]) 28 | @login_required 29 | def update_user_tagline(userId): 30 | user = User.query.get(userId) 31 | 32 | user.tag_line = request.json["tag_line"] 33 | 34 | db.session.add(user) 35 | db.session.commit() 36 | return user.to_dict() 37 | 38 | 39 | @user_routes.route('/description/', methods=["PUT"]) 40 | @login_required 41 | def update_user_description(userId): 42 | user = User.query.get(userId) 43 | 44 | user.description = request.json["description"] 45 | 46 | db.session.add(user) 47 | db.session.commit() 48 | return user.to_dict() 49 | 50 | 51 | @user_routes.route('/language/') 52 | @login_required 53 | def load_user_language(userId): 54 | languages = UserLanguage.query.filter(UserLanguage.user_id == userId).all() 55 | 56 | return jsonify([language.to_dict() for language in languages]) 57 | 58 | 59 | @user_routes.route('/language', methods=["POST"]) 60 | @login_required 61 | def add_user_language(): 62 | language = UserLanguage(**request.json) 63 | 64 | db.session.add(language) 65 | db.session.commit() 66 | return language.to_dict() 67 | 68 | 69 | @user_routes.route('/language/delete/', methods=["DELETE"]) 70 | @login_required 71 | def delete_user_language(id): 72 | userLanguage = UserLanguage.query.get(id) 73 | 74 | db.session.delete(userLanguage) 75 | db.session.commit() 76 | return userLanguage.to_dict() 77 | 78 | 79 | @user_routes.route('/skill', methods=["POST"]) 80 | @login_required 81 | def add_user_skill(): 82 | skill = Skill(**request.json) 83 | 84 | db.session.add(skill) 85 | db.session.commit() 86 | return skill.to_dict() 87 | 88 | 89 | @user_routes.route('/skill/') 90 | @login_required 91 | def load_user_skill(userId): 92 | skills = Skill.query.filter(Skill.user_id == userId).all() 93 | 94 | return jsonify([skill.to_dict() for skill in skills]) 95 | 96 | 97 | @user_routes.route('/skill/delete/', methods=["DELETE"]) 98 | @login_required 99 | def delete_user_skill(id): 100 | userSkill = Skill.query.get(id) 101 | 102 | db.session.delete(userSkill) 103 | db.session.commit() 104 | return userSkill.to_dict() -------------------------------------------------------------------------------- /app/aws.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import botocore 3 | import os 4 | import uuid 5 | 6 | BUCKET_NAME = os.environ.get("S3_BUCKET") 7 | S3_LOCATION = f"https://{BUCKET_NAME}.s3.amazonaws.com/" 8 | ALLOWED_EXTENSIONS = {"pdf", "png", "jpg", "jpeg", "gif"} 9 | 10 | s3 = boto3.client( 11 | "s3", 12 | aws_access_key_id=os.environ.get("S3_KEY"), 13 | aws_secret_access_key=os.environ.get("S3_SECRET") 14 | ) 15 | 16 | 17 | def allowed_file(filename): 18 | return "." in filename and \ 19 | filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS 20 | 21 | 22 | def get_unique_filename(filename): 23 | ext = filename.rsplit(".", 1)[1].lower() 24 | unique_filename = uuid.uuid4().hex 25 | return f"{unique_filename}.{ext}" 26 | 27 | 28 | def upload_file_to_s3(file, acl="public-read"): 29 | try: 30 | s3.upload_fileobj( 31 | file, 32 | BUCKET_NAME, 33 | file.filename, 34 | ExtraArgs={ 35 | "ACL": acl, 36 | "ContentType": file.content_type 37 | } 38 | ) 39 | except Exception as e: 40 | # in case the our s3 upload fails 41 | return {"errors": str(e)} 42 | 43 | return {"url": f"{S3_LOCATION}{file.filename}"} -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class Config: 4 | SECRET_KEY=os.environ.get('SECRET_KEY') 5 | SQLALCHEMY_TRACK_MODIFICATIONS=False 6 | SQLALCHEMY_DATABASE_URI=os.environ.get('DATABASE_URL') 7 | SQLALCHEMY_ECHO=True -------------------------------------------------------------------------------- /app/forms/__init__.py: -------------------------------------------------------------------------------- 1 | from .login_form import LoginForm 2 | from .signup_form import SignUpForm -------------------------------------------------------------------------------- /app/forms/login_form.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField 3 | from wtforms.validators import DataRequired, Email, ValidationError 4 | from app.models import User 5 | 6 | 7 | def user_exists(form, field): 8 | print("Checking if user exists", field.data) 9 | email = field.data 10 | user = User.query.filter(User.email == email).first() 11 | if not user: 12 | raise ValidationError("Email provided not found.") 13 | 14 | 15 | def password_matches(form, field): 16 | print("Checking if password matches") 17 | password = field.data 18 | email = form.data['email'] 19 | user = User.query.filter(User.email == email).first() 20 | if not user: 21 | raise ValidationError("No such user exists.") 22 | if not user.check_password(password): 23 | raise ValidationError("Password was incorrect.") 24 | 25 | 26 | class LoginForm(FlaskForm): 27 | email = StringField('email', validators=[DataRequired(), user_exists]) 28 | password = StringField('password', validators=[ 29 | DataRequired(), password_matches]) 30 | -------------------------------------------------------------------------------- /app/forms/signup_form.py: -------------------------------------------------------------------------------- 1 | from flask_wtf import FlaskForm 2 | from wtforms import StringField 3 | from wtforms.validators import DataRequired, Email, ValidationError 4 | from app.models import User 5 | 6 | 7 | def user_exists(form, field): 8 | print("Checking if user exits", field.data) 9 | email = field.data 10 | user = User.query.filter(User.email == email).first() 11 | if user: 12 | raise ValidationError("User is already registered.") 13 | 14 | 15 | class SignUpForm(FlaskForm): 16 | username = StringField('username', validators=[DataRequired()]) 17 | email = StringField('email', validators=[DataRequired(), user_exists]) 18 | password = StringField('password', validators=[DataRequired()]) 19 | -------------------------------------------------------------------------------- /app/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | from .user import User 3 | from .language import Language 4 | from .user_language import UserLanguage 5 | from .skill import Skill 6 | from .education import Education 7 | from .category import Category 8 | from .service_language import ServiceLanguage 9 | from .web import Web 10 | from .web_package import WebPackage 11 | from .bug import Bug 12 | from .bug_package import BugPackage 13 | from .service import Service 14 | from .requirement import Requirement 15 | from .review import Review 16 | from .service_requirement import ServiceRequirement 17 | from .req_answer import ReqAnswer 18 | -------------------------------------------------------------------------------- /app/models/bug.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | from .bug_package import BugPackage 3 | 4 | 5 | class Bug(db.Model): 6 | __tablename__ = "Bugs" 7 | 8 | id = db.Column(db.Integer, primary_key=True) 9 | type = db.Column(db.String, nullable=False) 10 | title = db.Column(db.String(87), nullable=False) 11 | description = db.Column(db.String, nullable=False) 12 | delivery_time = db.Column(db.Integer, nullable=False) 13 | bug_investigation = db.Column(db.Boolean()) 14 | fix_documentation = db.Column(db.Boolean()) 15 | detailed_code = db.Column(db.Boolean()) 16 | revisions = db.Column(db.Integer, nullable=False) 17 | price = db.Column(db.Float, nullable=False) 18 | 19 | bug_package_basic = db.relationship( 20 | "BugPackage", 21 | foreign_keys=[BugPackage.bug_basic_id], 22 | back_populates="bug_basic" 23 | ) 24 | 25 | bug_package_standard = db.relationship( 26 | "BugPackage", 27 | foreign_keys=[BugPackage.bug_standard_id], 28 | back_populates="bug_standard" 29 | ) 30 | 31 | bug_package_premium = db.relationship( 32 | "BugPackage", 33 | foreign_keys=[BugPackage.bug_premium_id], 34 | back_populates="bug_premium" 35 | ) 36 | 37 | def to_dict(self): 38 | return { 39 | "id": self.id, 40 | "type": self.type, 41 | "title": self.title, 42 | "description": self.description, 43 | "delivery_time": self.delivery_time, 44 | "bug_investigation": self.bug_investigation, 45 | "fix_documentation": self.fix_documentation, 46 | "detailed_code": self.detailed_code, 47 | "revisions": self.revisions, 48 | "price": self.price, 49 | } 50 | -------------------------------------------------------------------------------- /app/models/bug_package.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class BugPackage(db.Model): 5 | __tablename__ = "BugPackages" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | bug_basic_id = db.Column( 9 | db.Integer, db.ForeignKey("Bugs.id"), nullable=False) 10 | bug_standard_id = db.Column(db.Integer, db.ForeignKey("Bugs.id")) 11 | bug_premium_id = db.Column(db.Integer, db.ForeignKey("Bugs.id")) 12 | 13 | service = db.relationship( 14 | "Service", uselist=False, 15 | back_populates="bug_package" 16 | ) 17 | 18 | bug_basic = db.relationship( 19 | "Bug", 20 | foreign_keys=[bug_basic_id], 21 | back_populates="bug_package_basic" 22 | ) 23 | 24 | bug_standard = db.relationship( 25 | "Bug", 26 | foreign_keys=[bug_standard_id], 27 | back_populates="bug_package_standard" 28 | ) 29 | 30 | bug_premium = db.relationship( 31 | "Bug", 32 | foreign_keys=[bug_premium_id], 33 | back_populates="bug_package_premium" 34 | ) 35 | 36 | def to_dict(self): 37 | if self.bug_standard and self.bug_premium: 38 | return { 39 | "id": self.id, 40 | "bug_basic": self.bug_basic.to_dict(), 41 | "bug_standard": self.bug_standard.to_dict(), 42 | "bug_premium": self.bug_premium.to_dict(), 43 | } 44 | elif self.web_standard: 45 | return { 46 | "id": self.id, 47 | "bug_basic": self.bug_basic.to_dict(), 48 | "bug_standard": self.bug_standard.to_dict(), 49 | } 50 | else: 51 | return { 52 | "id": self.id, 53 | "bug_basic": self.bug_basic.to_dict(), 54 | } 55 | -------------------------------------------------------------------------------- /app/models/category.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Category(db.Model): 5 | __tablename__ = "Categories" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String, nullable=False) 9 | 10 | service = db.relationship("Service", back_populates="category") 11 | 12 | def to_dict(self): 13 | return { 14 | "id": self.id, 15 | "name": self.name 16 | } 17 | -------------------------------------------------------------------------------- /app/models/db.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | db = SQLAlchemy() 3 | -------------------------------------------------------------------------------- /app/models/education.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | class Education(db.Model): 4 | __tablename__ = 'Educations' 5 | 6 | id = db.Column(db.Integer, primary_key=True) 7 | name = db.Column(db.String, nullable=False) 8 | start_year = db.Column(db.Integer, nullable=False) 9 | end_year = db.Column(db.Integer, nullable=False) 10 | user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False) 11 | 12 | user = db.relationship("User", back_populates="education") 13 | 14 | def to_dict(self): 15 | return { 16 | "id": self.id, 17 | "name": self.name, 18 | "start_year": self.start_year, 19 | "end_year": self.end_year, 20 | "user_id": self.user_id, 21 | 22 | } -------------------------------------------------------------------------------- /app/models/language.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | class Language(db.Model): 4 | __tablename__ = 'Languages' 5 | 6 | id = db.Column(db.Integer, primary_key = True) 7 | name = db.Column(db.String, unique = True, nullable = False) 8 | 9 | user_language = db.relationship('UserLanguage', back_populates="language") 10 | 11 | def to_dict(self): 12 | return { 13 | "id": self.id, 14 | "name": self.name, 15 | } -------------------------------------------------------------------------------- /app/models/req_answer.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class ReqAnswer(db.Model): 5 | __tablename__ = "ReqAnswers" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | answer = db.Column(db.String, nullable=False) 9 | question_id = db.Column(db.Integer, db.ForeignKey("Requirements.id"), nullable=False) 10 | user_id = db.Column(db.Integer, db.ForeignKey("Users.id"), nullable=False) 11 | 12 | 13 | requirement = db.relationship("Requirement", back_populates="req_answer") 14 | user = db.relationship("User", back_populates="req_answer") 15 | 16 | 17 | def to_dict(self): 18 | return { 19 | "id": self.id, 20 | "requirement_id": self.requirement_id, 21 | "service_id": self.service_id 22 | } 23 | -------------------------------------------------------------------------------- /app/models/requirement.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Requirement(db.Model): 5 | __tablename__ = "Requirements" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | question = db.Column(db.String, nullable=False) 9 | 10 | service_requirement = db.relationship("ServiceRequirement", back_populates="requirement") 11 | req_answer = db.relationship("ReqAnswer", back_populates="requirement") 12 | 13 | 14 | 15 | def to_dict(self): 16 | return { 17 | "id": self.id, 18 | "question": self.question, 19 | } 20 | -------------------------------------------------------------------------------- /app/models/review.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Review(db.Model): 5 | __tablename__ = 'Reviews' 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | service_id = db.Column(db.Integer, db.ForeignKey( 9 | 'Services.id'), nullable=False) 10 | description = db.Column(db.String, nullable=False) 11 | score = db.Column(db.Integer, nullable=False) 12 | owner = db.Column(db.String, nullable=False) 13 | date_created = db.Column(db.Date(), 14 | server_default=db.func.now()) 15 | 16 | service = db.relationship("Service", back_populates="reviews") 17 | 18 | def to_dict(self): 19 | return { 20 | "id": self.id, 21 | "service_id": self.service_id, 22 | "description": self.description, 23 | "score": self.score, 24 | "owner": self.owner, 25 | "date_created": self.date_created, 26 | } 27 | -------------------------------------------------------------------------------- /app/models/service.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Service(db.Model): 5 | __tablename__ = "Services" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | publish = db.Column(db.Boolean()) 9 | title = db.Column(db.String, nullable=False) 10 | description = db.Column(db.String) 11 | listing_img = db.Column(db.String) 12 | user_id = db.Column(db.Integer, db.ForeignKey("Users.id"), nullable=False) 13 | category_id = db.Column(db.Integer, db.ForeignKey( 14 | "Categories.id"), nullable=False) 15 | service_language_id = db.Column( 16 | db.Integer, db.ForeignKey("ServiceLanguages.id")) 17 | web_package_id = db.Column(db.Integer, db.ForeignKey("WebPackages.id")) 18 | bug_package_id = db.Column(db.Integer, db.ForeignKey("BugPackages.id")) 19 | time_created = db.Column(db.DateTime(timezone=True), 20 | server_default=db.func.now()) 21 | 22 | user = db.relationship("User", back_populates="service") 23 | category = db.relationship("Category", back_populates="service") 24 | service_language = db.relationship( 25 | "ServiceLanguage", back_populates="service") 26 | web_package = db.relationship("WebPackage", back_populates="service") 27 | bug_package = db.relationship("BugPackage", back_populates="service") 28 | service_requirement = db.relationship( 29 | "ServiceRequirement", back_populates="service") 30 | reviews = db.relationship("Review", back_populates="service") 31 | 32 | def to_dict(self): 33 | if self.web_package_id: 34 | return { 35 | "id": self.id, 36 | "publish": self.publish, 37 | "title": self.title, 38 | "description": self.description, 39 | "listing_img": self.listing_img, 40 | "user_id": self.user_id, 41 | "category_id": self.category_id, 42 | "service_language_id": self.service_language_id, 43 | "web_package_id": self.web_package_id, 44 | "time_created": self.time_created, 45 | "user": self.user.to_dict(), 46 | "reviews": [review.to_dict() for review in self.reviews], 47 | "web_package": self.web_package.to_dict(), 48 | "service_language": self.service_language.to_dict() 49 | } 50 | else: 51 | return { 52 | "id": self.id, 53 | "publish": self.publish, 54 | "title": self.title, 55 | "description": self.description, 56 | "listing_img": self.listing_img, 57 | "user_id": self.user_id, 58 | "category_id": self.category_id, 59 | "service_language_id": self.service_language_id, 60 | "time_created": self.time_created, 61 | "user": self.user.to_dict(), 62 | "reviews": [review.to_dict() for review in self.reviews], 63 | "service_language": self.service_language.to_dict() 64 | } 65 | 66 | def dict_overview(self): 67 | return { 68 | "id": self.id, 69 | "title": self.title, 70 | "listing_img": self.listing_img, 71 | "user_id": self.user_id, 72 | "category_id": self.category_id, 73 | "service_language_id": self.service_language_id, 74 | "time_created": self.time_created, 75 | "user": self.user.to_dict(), 76 | } 77 | 78 | def bug_dict(self): 79 | if self.bug_package_id: 80 | return { 81 | "id": self.id, 82 | "publish": self.publish, 83 | "title": self.title, 84 | "description": self.description, 85 | "listing_img": self.listing_img, 86 | "user_id": self.user_id, 87 | "category_id": self.category_id, 88 | "service_language_id": self.service_language_id, 89 | "web_package_id": self.web_package_id, 90 | "time_created": self.time_created, 91 | "user": self.user.to_dict(), 92 | "reviews": [review.to_dict() for review in self.reviews], 93 | "bug_package": self.bug_package.to_dict(), 94 | "service_language": self.service_language.to_dict() 95 | } 96 | else: 97 | return { 98 | "id": self.id, 99 | "publish": self.publish, 100 | "title": self.title, 101 | "description": self.description, 102 | "listing_img": self.listing_img, 103 | "user_id": self.user_id, 104 | "category_id": self.category_id, 105 | "service_language_id": self.service_language_id, 106 | "time_created": self.time_created, 107 | "user": self.user.to_dict(), 108 | "reviews": [review.to_dict() for review in self.reviews], 109 | "service_language": self.service_language.to_dict() 110 | } 111 | -------------------------------------------------------------------------------- /app/models/service_language.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class ServiceLanguage(db.Model): 5 | __tablename__ = "ServiceLanguages" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String, default="Not Defined", nullable=False) 9 | 10 | service = db.relationship("Service", back_populates="service_language") 11 | 12 | def to_dict(self): 13 | return { 14 | "id": self.id, 15 | "name": self.name, 16 | } 17 | -------------------------------------------------------------------------------- /app/models/service_requirement.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class ServiceRequirement(db.Model): 5 | __tablename__ = "ServiceRequirements" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | requirement_id = db.Column(db.Integer, db.ForeignKey("Requirements.id"), nullable=False) 9 | service_id = db.Column(db.Integer, db.ForeignKey("Services.id"), nullable=False) 10 | 11 | requirement = db.relationship("Requirement", back_populates="service_requirement") 12 | service = db.relationship("Service", back_populates="service_requirement") 13 | 14 | 15 | def to_dict(self): 16 | return { 17 | "id": self.id, 18 | "requirement_id": self.requirement_id, 19 | "service_id": self.service_id 20 | } 21 | -------------------------------------------------------------------------------- /app/models/skill.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class Skill(db.Model): 5 | __tablename__ = 'Skills' 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | name = db.Column(db.String, nullable=False) 9 | level = db.Column(db.String, nullable=False) 10 | user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False) 11 | 12 | # user_skill = db.relationship('UserSkill', back_populates="skill") 13 | user = db.relationship("User", back_populates="skill") 14 | 15 | 16 | 17 | def to_dict(self): 18 | return { 19 | "id": self.id, 20 | "name": self.name, 21 | "level": self.level, 22 | "user_id": self.user_id, 23 | } 24 | -------------------------------------------------------------------------------- /app/models/user.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | from werkzeug.security import generate_password_hash, check_password_hash 3 | from flask_login import UserMixin 4 | 5 | 6 | class User(db.Model, UserMixin): 7 | __tablename__ = 'Users' 8 | 9 | id = db.Column(db.Integer, primary_key=True) 10 | seller = db.Column(db.Boolean(create_constraint=False,)) 11 | username = db.Column(db.String(80), nullable=False, unique=True) 12 | email = db.Column(db.String(255), nullable=False, unique=True) 13 | hashed_password = db.Column(db.String(255), nullable=False) 14 | profile_img = db.Column(db.String(500)) 15 | tag_line = db.Column(db.String()) 16 | description = db.Column(db.String()) 17 | date_created = db.Column(db.Date(), 18 | server_default=db.func.now()) 19 | 20 | language = db.relationship("UserLanguage", back_populates="user") 21 | skill = db.relationship("Skill", back_populates="user") 22 | education = db.relationship("Education", back_populates="user") 23 | service = db.relationship("Service", back_populates="user") 24 | req_answer = db.relationship("ReqAnswer", back_populates="user") 25 | 26 | 27 | @property 28 | def password(self): 29 | return self.hashed_password 30 | 31 | @password.setter 32 | def password(self, password): 33 | self.hashed_password = generate_password_hash(password) 34 | 35 | def check_password(self, password): 36 | return check_password_hash(self.password, password) 37 | 38 | def to_dict(self): 39 | return { 40 | "id": self.id, 41 | "seller": self.seller, 42 | "username": self.username, 43 | "email": self.email, 44 | "profile_img": self.profile_img, 45 | "tag_line": self.tag_line, 46 | "description": self.description, 47 | "date_created": self.date_created, 48 | } 49 | -------------------------------------------------------------------------------- /app/models/user_language.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class UserLanguage(db.Model): 5 | __tablename__ = 'UserLanguages' 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | level = db.Column(db.String, nullable = False) 9 | user_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False) 10 | language_id = db.Column(db.Integer, db.ForeignKey( 11 | 'Languages.id'), nullable=False) 12 | 13 | user = db.relationship("User", back_populates="language") 14 | language = db.relationship('Language', back_populates="user_language", lazy='subquery') 15 | 16 | def to_dict(self): 17 | return { 18 | "id": self.id, 19 | "level": self.level, 20 | "user_id": self.user_id, 21 | "language_id": self.language_id, 22 | "language": self.language.to_dict() 23 | } 24 | -------------------------------------------------------------------------------- /app/models/web.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | from .web_package import WebPackage 3 | 4 | 5 | class Web(db.Model): 6 | __tablename__ = "Webs" 7 | 8 | id = db.Column(db.Integer, primary_key=True) 9 | type = db.Column(db.String, nullable=False) 10 | title = db.Column(db.String(87), nullable=False) 11 | description = db.Column(db.String, nullable=False) 12 | delivery_time = db.Column(db.Integer, nullable=False) 13 | pages = db.Column(db.Integer, nullable=False) 14 | design_custom = db.Column(db.Boolean()) 15 | content_upload = db.Column(db.Boolean()) 16 | responsive_design = db.Column(db.Boolean()) 17 | source_code = db.Column(db.Boolean()) 18 | revisions = db.Column(db.Integer, nullable=False) 19 | price = db.Column(db.Float, nullable=False) 20 | 21 | web_package_basic = db.relationship( 22 | "WebPackage", 23 | foreign_keys=[WebPackage.web_basic_id], 24 | back_populates="web_basic" 25 | ) 26 | 27 | web_package_standard = db.relationship( 28 | "WebPackage", 29 | foreign_keys=[WebPackage.web_standard_id], 30 | back_populates="web_standard" 31 | ) 32 | 33 | web_package_premium = db.relationship( 34 | "WebPackage", 35 | foreign_keys=[WebPackage.web_premium_id], 36 | back_populates="web_premium" 37 | ) 38 | 39 | def to_dict(self): 40 | return { 41 | "id": self.id, 42 | "type": self.type, 43 | "title": self.title, 44 | "description": self.description, 45 | "delivery_time": self.delivery_time, 46 | "pages": self.pages, 47 | "design_custom": self.design_custom, 48 | "content_upload": self.content_upload, 49 | "responsive_design": self.responsive_design, 50 | "source_code": self.source_code, 51 | "revisions": self.revisions, 52 | "price": self.price, 53 | } 54 | -------------------------------------------------------------------------------- /app/models/web_package.py: -------------------------------------------------------------------------------- 1 | from .db import db 2 | 3 | 4 | class WebPackage(db.Model): 5 | __tablename__ = "WebPackages" 6 | 7 | id = db.Column(db.Integer, primary_key=True) 8 | web_basic_id = db.Column( 9 | db.Integer, db.ForeignKey("Webs.id"), nullable=False) 10 | web_standard_id = db.Column(db.Integer, db.ForeignKey("Webs.id")) 11 | web_premium_id = db.Column(db.Integer, db.ForeignKey("Webs.id")) 12 | 13 | service = db.relationship( 14 | "Service", uselist=False, 15 | back_populates="web_package" 16 | ) 17 | 18 | web_basic = db.relationship( 19 | "Web", 20 | foreign_keys=[web_basic_id], 21 | back_populates="web_package_basic" 22 | ) 23 | 24 | web_standard = db.relationship( 25 | "Web", 26 | foreign_keys=[web_standard_id], 27 | back_populates="web_package_standard" 28 | ) 29 | 30 | web_premium = db.relationship( 31 | "Web", 32 | foreign_keys=[web_premium_id], 33 | back_populates="web_package_premium" 34 | ) 35 | 36 | def to_dict(self): 37 | if self.web_standard and self.web_premium: 38 | return { 39 | "id": self.id, 40 | "web_basic": self.web_basic.to_dict(), 41 | "web_standard": self.web_standard.to_dict(), 42 | "web_premium": self.web_premium.to_dict(), 43 | } 44 | elif self.web_standard: 45 | return { 46 | "id": self.id, 47 | "web_basic": self.web_basic.to_dict(), 48 | "web_standard": self.web_standard.to_dict(), 49 | } 50 | else: 51 | return { 52 | "id": self.id, 53 | "web_basic": self.web_basic.to_dict(), 54 | } 55 | -------------------------------------------------------------------------------- /app/seeds/__init__.py: -------------------------------------------------------------------------------- 1 | from flask.cli import AppGroup 2 | from .users import seed_users, undo_users 3 | from .languages import seed_languages, undo_languages 4 | from .reviews import seed_reviews, undo_reviews 5 | from .categories import seed_categories, undo_categories 6 | from .service_languages import seed_service_languages, undo_service_languages 7 | from .webs import seed_webs, undo_webs 8 | from .web_packages import seed_web_packages, undo_web_packages 9 | from .bugs import seed_bugs, undo_bugs 10 | from .bug_packages import seed_bug_packages, undo_bug_packages 11 | from .services import seed_services, undo_services 12 | # from .languages import seed_languages, undo_languages 13 | # Creates a seed group to hold our commands 14 | # So we can type `flask seed --help` 15 | seed_commands = AppGroup('seed') 16 | 17 | # Creates the `flask seed all` command 18 | @seed_commands.command('all') 19 | def seed(): 20 | seed_users() 21 | seed_categories() 22 | seed_languages() 23 | seed_service_languages() 24 | seed_webs() 25 | seed_web_packages() 26 | seed_bugs() 27 | seed_bug_packages() 28 | seed_services() 29 | seed_reviews() 30 | # Add other seed functions here 31 | 32 | # Creates the `flask seed undo` command 33 | @seed_commands.command('undo') 34 | def undo(): 35 | undo_users() 36 | undo_categories() 37 | undo_languages() 38 | undo_service_languages() 39 | undo_webs() 40 | undo_web_packages() 41 | undo_bugs() 42 | undo_bug_packages() 43 | undo_services() 44 | undo_reviews() 45 | # Add other undo functions here 46 | -------------------------------------------------------------------------------- /app/seeds/bug_packages.py: -------------------------------------------------------------------------------- 1 | from app.models import db, BugPackage 2 | 3 | 4 | def seed_bug_packages(): 5 | 6 | bug_package_1 = BugPackage(bug_basic_id=1) 7 | bug_package_2 = BugPackage(bug_basic_id=2) 8 | bug_package_3 = BugPackage( 9 | bug_basic_id=3, bug_standard_id=26, bug_premium_id=38) 10 | bug_package_4 = BugPackage( 11 | bug_basic_id=4, bug_standard_id=27, bug_premium_id=39) 12 | bug_package_5 = BugPackage(bug_basic_id=5) 13 | bug_package_6 = BugPackage(bug_basic_id=6) 14 | bug_package_7 = BugPackage( 15 | bug_basic_id=7, bug_standard_id=28, bug_premium_id=40) 16 | bug_package_8 = BugPackage( 17 | bug_basic_id=8, bug_standard_id=29, bug_premium_id=41) 18 | bug_package_9 = BugPackage( 19 | bug_basic_id=9, bug_standard_id=30, bug_premium_id=42) 20 | bug_package_10 = BugPackage(bug_basic_id=10) 21 | bug_package_11 = BugPackage( 22 | bug_basic_id=11, bug_standard_id=31, bug_premium_id=43) 23 | bug_package_12 = BugPackage( 24 | bug_basic_id=12, bug_standard_id=32, bug_premium_id=44) 25 | bug_package_13 = BugPackage(bug_basic_id=13) 26 | bug_package_14 = BugPackage( 27 | bug_basic_id=14, bug_standard_id=33, bug_premium_id=45) 28 | bug_package_15 = BugPackage( 29 | bug_basic_id=15, bug_standard_id=34, bug_premium_id=46) 30 | bug_package_16 = BugPackage( 31 | bug_basic_id=16, bug_standard_id=35, bug_premium_id=47) 32 | bug_package_17 = BugPackage( 33 | bug_basic_id=17, bug_standard_id=36, bug_premium_id=48) 34 | bug_package_18 = BugPackage( 35 | bug_basic_id=18, bug_standard_id=37, bug_premium_id=49) 36 | bug_package_19 = BugPackage(bug_basic_id=19) 37 | bug_package_20 = BugPackage(bug_basic_id=20) 38 | bug_package_21 = BugPackage(bug_basic_id=21) 39 | bug_package_22 = BugPackage(bug_basic_id=22) 40 | bug_package_23 = BugPackage(bug_basic_id=23) 41 | bug_package_24 = BugPackage(bug_basic_id=24) 42 | bug_package_25 = BugPackage(bug_basic_id=25) 43 | 44 | db.session.add(bug_package_1) 45 | db.session.add(bug_package_2) 46 | db.session.add(bug_package_3) 47 | db.session.add(bug_package_4) 48 | db.session.add(bug_package_5) 49 | db.session.add(bug_package_6) 50 | db.session.add(bug_package_7) 51 | db.session.add(bug_package_8) 52 | db.session.add(bug_package_9) 53 | db.session.add(bug_package_10) 54 | db.session.add(bug_package_11) 55 | db.session.add(bug_package_12) 56 | db.session.add(bug_package_13) 57 | db.session.add(bug_package_14) 58 | db.session.add(bug_package_15) 59 | db.session.add(bug_package_16) 60 | db.session.add(bug_package_17) 61 | db.session.add(bug_package_18) 62 | db.session.add(bug_package_19) 63 | db.session.add(bug_package_20) 64 | db.session.add(bug_package_21) 65 | db.session.add(bug_package_22) 66 | db.session.add(bug_package_23) 67 | db.session.add(bug_package_24) 68 | db.session.add(bug_package_25) 69 | 70 | db.session.commit() 71 | 72 | 73 | # Uses a raw SQL query to TRUNCATE the users table. 74 | # SQLAlchemy doesn't have a built in function to do this 75 | # TRUNCATE Removes all the data from the table, and resets 76 | # the auto incrementing primary key 77 | def undo_bug_packages(): 78 | db.session.execute('TRUNCATE BugPackages RESTART IDENTITY CASCADE;') 79 | db.session.commit() 80 | -------------------------------------------------------------------------------- /app/seeds/categories.py: -------------------------------------------------------------------------------- 1 | from app.models import db, Category 2 | 3 | 4 | def seed_categories(): 5 | 6 | web_application = Category(name="Web Application") 7 | db.session.add(web_application) 8 | 9 | custom_website = Category(name="Custom Website") 10 | db.session.add(custom_website) 11 | 12 | bug_fixes = Category(name="Bug Fixes") 13 | db.session.add(bug_fixes) 14 | 15 | scripting = Category(name="Scripting") 16 | db.session.add(scripting) 17 | 18 | # convert_psd = Category(name="Convert PSD") 19 | # db.session.add(convert_psd) 20 | 21 | # landing_page = Category(name="Landing Page") 22 | # db.session.add(landing_page) 23 | 24 | # email_template = Category(name="Email Template") 25 | # db.session.add(email_template) 26 | 27 | # browser_extensions = Category(name="Browser Extensions") 28 | # db.session.add(browser_extensions) 29 | 30 | # help_consultation = Category(name="Help Consultation") 31 | # db.session.add(help_consultation) 32 | 33 | 34 | 35 | 36 | 37 | db.session.commit() 38 | 39 | 40 | # Uses a raw SQL query to TRUNCATE the users table. 41 | # SQLAlchemy doesn't have a built in function to do this 42 | # TRUNCATE Removes all the data from the table, and resets 43 | # the auto incrementing primary key 44 | def undo_categories(): 45 | db.session.execute('TRUNCATE Categories RESTART IDENTITY CASCADE;') 46 | db.session.commit() 47 | -------------------------------------------------------------------------------- /app/seeds/languages.py: -------------------------------------------------------------------------------- 1 | from app.models import db, Language 2 | 3 | 4 | # Adds a demo user, you can add other users here if you want 5 | def seed_languages(): 6 | 7 | languages = [ 8 | "Mandarin Chinese", "English", "Spanish", "Hindi", "French", "Portuguese", "Bengali", "Standard Arabic", "Russian", "Indonesian", 9 | "Urdu", "Standard German", "Japanese", "Swahili", "Western Punjabi", "Javanese", "Wu Chinese", "Telugu", "Korean", "Tamil", 10 | "Marathi", "Turkish", "Vietnamese", "Italian", "Yue Chinese", "Thai", "Egyptian Spoken Arabic", "Iranian Persian", "Huizhou Chinese", "Min Nan Chinese", 11 | "Gujarati", "Kannada", "Jinyu Chinese", "Filipino", "Burmese", "Hausa", "Polish", "Bhojpuri", "Xiang Chinese", "Ukrainian", 12 | "Malayalam", "Sunda", "Maithili", "Odia", "Algerian Spoken Arabic", "Hakka Chinese", "Nigerian Pidgin", "Eastern Punjabi", "Moroccan Spoken Arabic", "Zulu", 13 | "North Levantine Spoken Arabic", "Amharic", "Tagalog", "Northern Uzbek", "Sindhi", "Romanian", "Dutch", "Gan Chinese", "Northern Pashto", "Yoruba", 14 | "Sa’idi Spoken Arabic", "Saraiki", "Xhosa", "Malay", "Igbo", "Afrikaans", "Sudanese Spoken Arabic", "Sinhala", "Cebuano", "Mesopotamian Spoken Arabic", 15 | "Nepali", "Rangpuri", "Central Khmer", "Northern Kurdish", "Northeastern Thai", "South Azerbaijani", "Somali", "Bamanankan", "Bavarian", "Magahi", 16 | "Northern Sotho", "Southern Sotho", "Chhattisgarhi", "Tswana", "Czech", "Greek", "Chittagonian", "Assamese", "Deccan", "Kazakh", 17 | "Hungarian", "Shona", "Jula", "Swedish", "Sylheti", "Najdi Spoken Arabic", "Nigerian Fulfulde", "Tunisian Spoken Arabic", "Kinyarwanda", "Min Bei Chinese", 18 | 19 | 20 | ] 21 | 22 | for one in languages: 23 | lang = Language( 24 | name=one 25 | ) 26 | db.session.add(lang) 27 | db.session.commit() 28 | 29 | 30 | # Uses a raw SQL query to TRUNCATE the users table. 31 | # SQLAlchemy doesn't have a built in function to do this 32 | # TRUNCATE Removes all the data from the table, and resets 33 | # the auto incrementing primary key 34 | def undo_languages(): 35 | db.session.execute('TRUNCATE Languages RESTART IDENTITY CASCADE;') 36 | db.session.commit() 37 | -------------------------------------------------------------------------------- /app/seeds/service_languages.py: -------------------------------------------------------------------------------- 1 | from app.models import db, ServiceLanguage 2 | 3 | 4 | # Adds a demo user, you can add other users here if you want 5 | def seed_service_languages(): 6 | 7 | not_defined = ServiceLanguage(name="Not Defined") 8 | db.session.add(not_defined) 9 | 10 | asp_net = ServiceLanguage(name="ASP.NET") 11 | db.session.add(asp_net) 12 | 13 | html_css = ServiceLanguage(name="HTML & CSS") 14 | db.session.add(html_css) 15 | 16 | javascript = ServiceLanguage(name="JavaScript") 17 | db.session.add(javascript) 18 | 19 | perl = ServiceLanguage(name="Perl") 20 | db.session.add(perl) 21 | 22 | php = ServiceLanguage(name=" PHP") 23 | db.session.add(php) 24 | 25 | python = ServiceLanguage(name="Python") 26 | db.session.add(python) 27 | 28 | ruby_ror = ServiceLanguage(name="Ruby/RoR") 29 | db.session.add(ruby_ror) 30 | 31 | scala = ServiceLanguage(name="Scala") 32 | db.session.add(scala) 33 | 34 | flash = ServiceLanguage(name="Flash") 35 | db.session.add(flash) 36 | 37 | java = ServiceLanguage(name="Java") 38 | db.session.add(java) 39 | 40 | type_script = ServiceLanguage(name="TypeScript") 41 | db.session.add(type_script) 42 | 43 | c = ServiceLanguage(name="C#") 44 | db.session.add(c) 45 | 46 | go = ServiceLanguage(name="Go") 47 | db.session.add(go) 48 | 49 | kotlin = ServiceLanguage(name="Kotlin") 50 | db.session.add(kotlin) 51 | 52 | multi = ServiceLanguage(name="Multiple Languages") 53 | db.session.add(multi) 54 | db.session.commit() 55 | 56 | 57 | # Uses a raw SQL query to TRUNCATE the users table. 58 | # SQLAlchemy doesn't have a built in function to do this 59 | # TRUNCATE Removes all the data from the table, and resets 60 | # the auto incrementing primary key 61 | def undo_service_languages(): 62 | db.session.execute('TRUNCATE ServiceLanguages RESTART IDENTITY CASCADE;') 63 | db.session.commit() 64 | -------------------------------------------------------------------------------- /app/seeds/web_packages.py: -------------------------------------------------------------------------------- 1 | from app.models import db, WebPackage 2 | 3 | 4 | def seed_web_packages(): 5 | 6 | web_package_1 = WebPackage( 7 | web_basic_id=1, web_standard_id=29, web_premium_id=45) 8 | web_package_2 = WebPackage( 9 | web_basic_id=2, web_standard_id=30, web_premium_id=46) 10 | web_package_3 = WebPackage( 11 | web_basic_id=3, web_standard_id=31, web_premium_id=47) 12 | web_package_4 = WebPackage( 13 | web_basic_id=4, web_standard_id=32, web_premium_id=48) 14 | web_package_5 = WebPackage(web_basic_id=5) 15 | web_package_6 = WebPackage(web_basic_id=6) 16 | web_package_7 = WebPackage(web_basic_id=7) 17 | web_package_8 = WebPackage(web_basic_id=8) 18 | web_package_9 = WebPackage(web_basic_id=9) 19 | web_package_10 = WebPackage( 20 | web_basic_id=10, web_standard_id=33, web_premium_id=49) 21 | web_package_11 = WebPackage(web_basic_id=11) 22 | web_package_12 = WebPackage(web_basic_id=12) 23 | web_package_13 = WebPackage(web_basic_id=13) 24 | web_package_14 = WebPackage( 25 | web_basic_id=14, web_standard_id=34, web_premium_id=50) 26 | web_package_15 = WebPackage(web_basic_id=15) 27 | web_package_16 = WebPackage(web_basic_id=16) 28 | web_package_17 = WebPackage( 29 | web_basic_id=17, web_standard_id=35, web_premium_id=51) 30 | web_package_18 = WebPackage( 31 | web_basic_id=18, web_standard_id=36, web_premium_id=52) 32 | web_package_19 = WebPackage( 33 | web_basic_id=19, web_standard_id=37, web_premium_id=53) 34 | web_package_20 = WebPackage(web_basic_id=20) 35 | web_package_21 = WebPackage( 36 | web_basic_id=21, web_standard_id=38, web_premium_id=54) 37 | web_package_22 = WebPackage( 38 | web_basic_id=22, web_standard_id=39, web_premium_id=55) 39 | web_package_23 = WebPackage( 40 | web_basic_id=23, web_standard_id=40, web_premium_id=56) 41 | web_package_24 = WebPackage( 42 | web_basic_id=24, web_standard_id=41, web_premium_id=57) 43 | web_package_25 = WebPackage( 44 | web_basic_id=25, web_standard_id=42, web_premium_id=58) 45 | web_package_26 = WebPackage(web_basic_id=26) 46 | web_package_27 = WebPackage( 47 | web_basic_id=27, web_standard_id=43, web_premium_id=59) 48 | web_package_28 = WebPackage( 49 | web_basic_id=28, web_standard_id=44, web_premium_id=60) 50 | 51 | db.session.add(web_package_1) 52 | db.session.add(web_package_2) 53 | db.session.add(web_package_3) 54 | db.session.add(web_package_4) 55 | db.session.add(web_package_5) 56 | db.session.add(web_package_6) 57 | db.session.add(web_package_7) 58 | db.session.add(web_package_8) 59 | db.session.add(web_package_9) 60 | db.session.add(web_package_10) 61 | db.session.add(web_package_11) 62 | db.session.add(web_package_12) 63 | db.session.add(web_package_13) 64 | db.session.add(web_package_14) 65 | db.session.add(web_package_15) 66 | db.session.add(web_package_16) 67 | db.session.add(web_package_17) 68 | db.session.add(web_package_18) 69 | db.session.add(web_package_19) 70 | db.session.add(web_package_20) 71 | db.session.add(web_package_21) 72 | db.session.add(web_package_22) 73 | db.session.add(web_package_23) 74 | db.session.add(web_package_24) 75 | db.session.add(web_package_25) 76 | db.session.add(web_package_26) 77 | db.session.add(web_package_27) 78 | db.session.add(web_package_28) 79 | 80 | web_package_29 = WebPackage( 81 | web_basic_id=61, web_standard_id=88, web_premium_id=109) 82 | web_package_30 = WebPackage(web_basic_id=62) 83 | web_package_31 = WebPackage(web_basic_id=63) 84 | web_package_32 = WebPackage( 85 | web_basic_id=64, web_standard_id=89, web_premium_id=110) 86 | web_package_33 = WebPackage( 87 | web_basic_id=65, web_standard_id=90, web_premium_id=111) 88 | web_package_34 = WebPackage( 89 | web_basic_id=66, web_standard_id=91, web_premium_id=112) 90 | web_package_35 = WebPackage( 91 | web_basic_id=67, web_standard_id=92, web_premium_id=113) 92 | web_package_36 = WebPackage( 93 | web_basic_id=68, web_standard_id=93, web_premium_id=114) 94 | web_package_37 = WebPackage(web_basic_id=69) 95 | web_package_38 = WebPackage(web_basic_id=70) 96 | web_package_39 = WebPackage( 97 | web_basic_id=71, web_standard_id=94, web_premium_id=115) 98 | web_package_40 = WebPackage( 99 | web_basic_id=72, web_standard_id=95, web_premium_id=116) 100 | web_package_41 = WebPackage( 101 | web_basic_id=73, web_standard_id=96, web_premium_id=117) 102 | web_package_42 = WebPackage( 103 | web_basic_id=74, web_standard_id=97, web_premium_id=118) 104 | web_package_43 = WebPackage( 105 | web_basic_id=75, web_standard_id=98, web_premium_id=119) 106 | web_package_44 = WebPackage( 107 | web_basic_id=76, web_standard_id=99, web_premium_id=120) 108 | web_package_45 = WebPackage(web_basic_id=77) 109 | web_package_46 = WebPackage( 110 | web_basic_id=78, web_standard_id=100, web_premium_id=121) 111 | web_package_47 = WebPackage( 112 | web_basic_id=79, web_standard_id=101, web_premium_id=122) 113 | web_package_48 = WebPackage( 114 | web_basic_id=80, web_standard_id=102, web_premium_id=123) 115 | web_package_49 = WebPackage( 116 | web_basic_id=81, web_standard_id=103, web_premium_id=124) 117 | web_package_50 = WebPackage( 118 | web_basic_id=82, web_standard_id=104, web_premium_id=125) 119 | web_package_51 = WebPackage( 120 | web_basic_id=83, web_standard_id=105, web_premium_id=126) 121 | web_package_52 = WebPackage( 122 | web_basic_id=84, web_standard_id=106, web_premium_id=127) 123 | web_package_53 = WebPackage( 124 | web_basic_id=85, web_standard_id=107, web_premium_id=128) 125 | web_package_54 = WebPackage( 126 | web_basic_id=86, web_standard_id=108, web_premium_id=129) 127 | web_package_55 = WebPackage(web_basic_id=87) 128 | 129 | db.session.add(web_package_29) 130 | db.session.add(web_package_30) 131 | db.session.add(web_package_31) 132 | db.session.add(web_package_31) 133 | db.session.add(web_package_32) 134 | db.session.add(web_package_33) 135 | db.session.add(web_package_34) 136 | db.session.add(web_package_35) 137 | db.session.add(web_package_36) 138 | db.session.add(web_package_37) 139 | db.session.add(web_package_38) 140 | db.session.add(web_package_39) 141 | db.session.add(web_package_40) 142 | db.session.add(web_package_41) 143 | db.session.add(web_package_42) 144 | db.session.add(web_package_43) 145 | db.session.add(web_package_44) 146 | db.session.add(web_package_45) 147 | db.session.add(web_package_46) 148 | db.session.add(web_package_47) 149 | db.session.add(web_package_48) 150 | db.session.add(web_package_49) 151 | db.session.add(web_package_50) 152 | db.session.add(web_package_51) 153 | db.session.add(web_package_52) 154 | db.session.add(web_package_53) 155 | db.session.add(web_package_54) 156 | db.session.add(web_package_55) 157 | 158 | db.session.commit() 159 | 160 | 161 | # Uses a raw SQL query to TRUNCATE the users table. 162 | # SQLAlchemy doesn't have a built in function to do this 163 | # TRUNCATE Removes all the data from the table, and resets 164 | # the auto incrementing primary key 165 | def undo_web_packages(): 166 | db.session.execute('TRUNCATE WebPackages RESTART IDENTITY CASCADE;') 167 | db.session.commit() 168 | -------------------------------------------------------------------------------- /architecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixzzels/Programmerr/40c9d563e3a2187d4621c1b58bbd07e06aac5640/architecture.pdf -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2-binary==2.8.6 2 | -------------------------------------------------------------------------------- /flask-help.md: -------------------------------------------------------------------------------- 1 | # Flask React Project 2 | 3 | This is the backend for the Flask React project. 4 | 5 | ## Getting started 6 | 7 | 1. Clone this repository (only this branch) 8 | 9 | ```bash 10 | git clone https://github.com/appacademy-starters/python-project-starter.git 11 | ``` 12 | 13 | 2. Install dependencies 14 | 15 | ```bash 16 | pipenv install --dev -r dev-requirements.txt && pipenv install -r requirements.txt 17 | ``` 18 | 19 | 3. Create a **.env** file based on the example with proper settings for your 20 | development environment 21 | 4. Setup your PostgreSQL user, password and database and make sure it matches your **.env** file 22 | 23 | 5. Get into your pipenv, migrate your database, seed your database, and run your flask app 24 | 25 | ```bash 26 | pipenv shell 27 | ``` 28 | 29 | ```bash 30 | flask db upgrade 31 | ``` 32 | 33 | ```bash 34 | flask seed all 35 | ``` 36 | 37 | ```bash 38 | flask run 39 | ``` 40 | 41 | 6. To run the React App in development, checkout the [README](./react-app/README.md) inside the `react-app` directory. 42 | 43 | *** 44 | *IMPORTANT!* 45 | If you add any python dependencies to your pipfiles, you'll need to regenerate your requirements.txt before deployment. 46 | You can do this by running: 47 | 48 | ```bash 49 | pipenv lock -r > requirements.txt 50 | ``` 51 | 52 | *ALSO IMPORTANT!* 53 | psycopg2-binary MUST remain a dev dependency because you can't install it on apline-linux. 54 | There is a layer in the Dockerfile that will install psycopg2 (not binary) for us. 55 | *** 56 | 57 | ## Deploy to Heroku 58 | 59 | 1. Create a new project on Heroku 60 | 2. Under Resources click "Find more add-ons" and add the add on called "Heroku Postgres" 61 | 3. Install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-command-line) 62 | 4. Run 63 | 64 | ```bash 65 | heroku login 66 | ``` 67 | 68 | 5. Login to the heroku container registry 69 | 70 | ```bash 71 | heroku container:login 72 | ``` 73 | 74 | 6. Update the `REACT_APP_BASE_URL` variable in the Dockerfile. 75 | This should be the full URL of your Heroku app: i.e. "https://flask-react-aa.herokuapp.com" 76 | 7. Push your docker container to heroku from the root directory of your project. 77 | This will build the dockerfile and push the image to your heroku container registry 78 | 79 | ```bash 80 | heroku container:push web -a {NAME_OF_HEROKU_APP} 81 | ``` 82 | 83 | 8. Release your docker container to heroku 84 | 85 | ```bash 86 | heroku container:release web -a {NAME_OF_HEROKU_APP} 87 | ``` 88 | 89 | 9. set up your database: 90 | 91 | ```bash 92 | heroku run -a {NAME_OF_HEROKU_APP} flask db upgrade 93 | heroku run -a {NAME_OF_HEROKU_APP} flask seed all 94 | ``` 95 | 96 | 10. Under Settings find "Config Vars" and add any additional/secret .env variables. 97 | 98 | 11. profit 99 | -------------------------------------------------------------------------------- /migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d%%(second).2d_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = INFO 34 | handlers = 35 | qualname = alembic 36 | 37 | [handler_console] 38 | class = StreamHandler 39 | args = (sys.stderr,) 40 | level = NOTSET 41 | formatter = generic 42 | 43 | [formatter_generic] 44 | format = %(levelname)-5.5s [%(name)s] %(message)s 45 | datefmt = %H:%M:%S 46 | -------------------------------------------------------------------------------- /migrations/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | 3 | import logging 4 | from logging.config import fileConfig 5 | 6 | from sqlalchemy import engine_from_config 7 | from sqlalchemy import pool 8 | 9 | from alembic import context 10 | 11 | # this is the Alembic Config object, which provides 12 | # access to the values within the .ini file in use. 13 | config = context.config 14 | 15 | # Interpret the config file for Python logging. 16 | # This line sets up loggers basically. 17 | fileConfig(config.config_file_name) 18 | logger = logging.getLogger('alembic.env') 19 | 20 | # add your model's MetaData object here 21 | # for 'autogenerate' support 22 | # from myapp import mymodel 23 | # target_metadata = mymodel.Base.metadata 24 | from flask import current_app 25 | config.set_main_option( 26 | 'sqlalchemy.url', 27 | str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) 28 | target_metadata = current_app.extensions['migrate'].db.metadata 29 | 30 | # other values from the config, defined by the needs of env.py, 31 | # can be acquired: 32 | # my_important_option = config.get_main_option("my_important_option") 33 | # ... etc. 34 | 35 | 36 | def run_migrations_offline(): 37 | """Run migrations in 'offline' mode. 38 | 39 | This configures the context with just a URL 40 | and not an Engine, though an Engine is acceptable 41 | here as well. By skipping the Engine creation 42 | we don't even need a DBAPI to be available. 43 | 44 | Calls to context.execute() here emit the given string to the 45 | script output. 46 | 47 | """ 48 | url = config.get_main_option("sqlalchemy.url") 49 | context.configure( 50 | url=url, target_metadata=target_metadata, literal_binds=True 51 | ) 52 | 53 | with context.begin_transaction(): 54 | context.run_migrations() 55 | 56 | 57 | def run_migrations_online(): 58 | """Run migrations in 'online' mode. 59 | 60 | In this scenario we need to create an Engine 61 | and associate a connection with the context. 62 | 63 | """ 64 | 65 | # this callback is used to prevent an auto-migration from being generated 66 | # when there are no changes to the schema 67 | # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html 68 | def process_revision_directives(context, revision, directives): 69 | if getattr(config.cmd_opts, 'autogenerate', False): 70 | script = directives[0] 71 | if script.upgrade_ops.is_empty(): 72 | directives[:] = [] 73 | logger.info('No changes in schema detected.') 74 | 75 | connectable = engine_from_config( 76 | config.get_section(config.config_ini_section), 77 | prefix='sqlalchemy.', 78 | poolclass=pool.NullPool, 79 | ) 80 | 81 | with connectable.connect() as connection: 82 | context.configure( 83 | connection=connection, 84 | target_metadata=target_metadata, 85 | process_revision_directives=process_revision_directives, 86 | **current_app.extensions['migrate'].configure_args 87 | ) 88 | 89 | with context.begin_transaction(): 90 | context.run_migrations() 91 | 92 | 93 | if context.is_offline_mode(): 94 | run_migrations_offline() 95 | else: 96 | run_migrations_online() 97 | -------------------------------------------------------------------------------- /migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /migrations/versions/20210613_160626_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: bc6cf6b01653 4 | Revises: 5 | Create Date: 2021-06-13 16:06:26.756537 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'bc6cf6b01653' 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table('Bugs', 22 | sa.Column('id', sa.Integer(), nullable=False), 23 | sa.Column('type', sa.String(), nullable=False), 24 | sa.Column('title', sa.String(length=87), nullable=False), 25 | sa.Column('description', sa.String(), nullable=False), 26 | sa.Column('delivery_time', sa.Integer(), nullable=False), 27 | sa.Column('bug_investigation', sa.Boolean(), nullable=True), 28 | sa.Column('fix_documentation', sa.Boolean(), nullable=True), 29 | sa.Column('detailed_code', sa.Boolean(), nullable=True), 30 | sa.Column('revisions', sa.Integer(), nullable=False), 31 | sa.Column('price', sa.Float(), nullable=False), 32 | sa.PrimaryKeyConstraint('id') 33 | ) 34 | op.create_table('Categories', 35 | sa.Column('id', sa.Integer(), nullable=False), 36 | sa.Column('name', sa.String(), nullable=False), 37 | sa.PrimaryKeyConstraint('id') 38 | ) 39 | op.create_table('Languages', 40 | sa.Column('id', sa.Integer(), nullable=False), 41 | sa.Column('name', sa.String(), nullable=False), 42 | sa.PrimaryKeyConstraint('id'), 43 | sa.UniqueConstraint('name') 44 | ) 45 | op.create_table('Requirements', 46 | sa.Column('id', sa.Integer(), nullable=False), 47 | sa.Column('question', sa.String(), nullable=False), 48 | sa.PrimaryKeyConstraint('id') 49 | ) 50 | op.create_table('ServiceLanguages', 51 | sa.Column('id', sa.Integer(), nullable=False), 52 | sa.Column('name', sa.String(), nullable=False), 53 | sa.PrimaryKeyConstraint('id') 54 | ) 55 | op.create_table('Users', 56 | sa.Column('id', sa.Integer(), nullable=False), 57 | sa.Column('seller', sa.Boolean(create_constraint=False), nullable=True), 58 | sa.Column('username', sa.String(length=80), nullable=False), 59 | sa.Column('email', sa.String(length=255), nullable=False), 60 | sa.Column('hashed_password', sa.String(length=255), nullable=False), 61 | sa.Column('profile_img', sa.String(length=500), nullable=True), 62 | sa.Column('tag_line', sa.String(), nullable=True), 63 | sa.Column('description', sa.String(), nullable=True), 64 | sa.Column('date_created', sa.Date(), server_default=sa.text('now()'), nullable=True), 65 | sa.PrimaryKeyConstraint('id'), 66 | sa.UniqueConstraint('email'), 67 | sa.UniqueConstraint('username') 68 | ) 69 | op.create_table('Webs', 70 | sa.Column('id', sa.Integer(), nullable=False), 71 | sa.Column('type', sa.String(), nullable=False), 72 | sa.Column('title', sa.String(length=87), nullable=False), 73 | sa.Column('description', sa.String(), nullable=False), 74 | sa.Column('delivery_time', sa.Integer(), nullable=False), 75 | sa.Column('pages', sa.Integer(), nullable=False), 76 | sa.Column('design_custom', sa.Boolean(), nullable=True), 77 | sa.Column('content_upload', sa.Boolean(), nullable=True), 78 | sa.Column('responsive_design', sa.Boolean(), nullable=True), 79 | sa.Column('source_code', sa.Boolean(), nullable=True), 80 | sa.Column('revisions', sa.Integer(), nullable=False), 81 | sa.Column('price', sa.Float(), nullable=False), 82 | sa.PrimaryKeyConstraint('id') 83 | ) 84 | op.create_table('BugPackages', 85 | sa.Column('id', sa.Integer(), nullable=False), 86 | sa.Column('bug_basic_id', sa.Integer(), nullable=False), 87 | sa.Column('bug_standard_id', sa.Integer(), nullable=True), 88 | sa.Column('bug_premium_id', sa.Integer(), nullable=True), 89 | sa.ForeignKeyConstraint(['bug_basic_id'], ['Bugs.id'], ), 90 | sa.ForeignKeyConstraint(['bug_premium_id'], ['Bugs.id'], ), 91 | sa.ForeignKeyConstraint(['bug_standard_id'], ['Bugs.id'], ), 92 | sa.PrimaryKeyConstraint('id') 93 | ) 94 | op.create_table('Educations', 95 | sa.Column('id', sa.Integer(), nullable=False), 96 | sa.Column('name', sa.String(), nullable=False), 97 | sa.Column('start_year', sa.Integer(), nullable=False), 98 | sa.Column('end_year', sa.Integer(), nullable=False), 99 | sa.Column('user_id', sa.Integer(), nullable=False), 100 | sa.ForeignKeyConstraint(['user_id'], ['Users.id'], ), 101 | sa.PrimaryKeyConstraint('id') 102 | ) 103 | op.create_table('ReqAnswers', 104 | sa.Column('id', sa.Integer(), nullable=False), 105 | sa.Column('answer', sa.String(), nullable=False), 106 | sa.Column('question_id', sa.Integer(), nullable=False), 107 | sa.Column('user_id', sa.Integer(), nullable=False), 108 | sa.ForeignKeyConstraint(['question_id'], ['Requirements.id'], ), 109 | sa.ForeignKeyConstraint(['user_id'], ['Users.id'], ), 110 | sa.PrimaryKeyConstraint('id') 111 | ) 112 | op.create_table('Skills', 113 | sa.Column('id', sa.Integer(), nullable=False), 114 | sa.Column('name', sa.String(), nullable=False), 115 | sa.Column('level', sa.String(), nullable=False), 116 | sa.Column('user_id', sa.Integer(), nullable=False), 117 | sa.ForeignKeyConstraint(['user_id'], ['Users.id'], ), 118 | sa.PrimaryKeyConstraint('id') 119 | ) 120 | op.create_table('UserLanguages', 121 | sa.Column('id', sa.Integer(), nullable=False), 122 | sa.Column('level', sa.String(), nullable=False), 123 | sa.Column('user_id', sa.Integer(), nullable=False), 124 | sa.Column('language_id', sa.Integer(), nullable=False), 125 | sa.ForeignKeyConstraint(['language_id'], ['Languages.id'], ), 126 | sa.ForeignKeyConstraint(['user_id'], ['Users.id'], ), 127 | sa.PrimaryKeyConstraint('id') 128 | ) 129 | op.create_table('WebPackages', 130 | sa.Column('id', sa.Integer(), nullable=False), 131 | sa.Column('web_basic_id', sa.Integer(), nullable=False), 132 | sa.Column('web_standard_id', sa.Integer(), nullable=True), 133 | sa.Column('web_premium_id', sa.Integer(), nullable=True), 134 | sa.ForeignKeyConstraint(['web_basic_id'], ['Webs.id'], ), 135 | sa.ForeignKeyConstraint(['web_premium_id'], ['Webs.id'], ), 136 | sa.ForeignKeyConstraint(['web_standard_id'], ['Webs.id'], ), 137 | sa.PrimaryKeyConstraint('id') 138 | ) 139 | op.create_table('Services', 140 | sa.Column('id', sa.Integer(), nullable=False), 141 | sa.Column('publish', sa.Boolean(), nullable=True), 142 | sa.Column('title', sa.String(), nullable=False), 143 | sa.Column('description', sa.String(), nullable=True), 144 | sa.Column('listing_img', sa.String(), nullable=True), 145 | sa.Column('user_id', sa.Integer(), nullable=False), 146 | sa.Column('category_id', sa.Integer(), nullable=False), 147 | sa.Column('service_language_id', sa.Integer(), nullable=True), 148 | sa.Column('web_package_id', sa.Integer(), nullable=True), 149 | sa.Column('bug_package_id', sa.Integer(), nullable=True), 150 | sa.Column('time_created', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), 151 | sa.ForeignKeyConstraint(['bug_package_id'], ['BugPackages.id'], ), 152 | sa.ForeignKeyConstraint(['category_id'], ['Categories.id'], ), 153 | sa.ForeignKeyConstraint(['service_language_id'], ['ServiceLanguages.id'], ), 154 | sa.ForeignKeyConstraint(['user_id'], ['Users.id'], ), 155 | sa.ForeignKeyConstraint(['web_package_id'], ['WebPackages.id'], ), 156 | sa.PrimaryKeyConstraint('id') 157 | ) 158 | op.create_table('Reviews', 159 | sa.Column('id', sa.Integer(), nullable=False), 160 | sa.Column('service_id', sa.Integer(), nullable=False), 161 | sa.Column('description', sa.String(), nullable=False), 162 | sa.Column('score', sa.Integer(), nullable=False), 163 | sa.Column('owner', sa.String(), nullable=False), 164 | sa.Column('date_created', sa.Date(), server_default=sa.text('now()'), nullable=True), 165 | sa.ForeignKeyConstraint(['service_id'], ['Services.id'], ), 166 | sa.PrimaryKeyConstraint('id') 167 | ) 168 | op.create_table('ServiceRequirements', 169 | sa.Column('id', sa.Integer(), nullable=False), 170 | sa.Column('requirement_id', sa.Integer(), nullable=False), 171 | sa.Column('service_id', sa.Integer(), nullable=False), 172 | sa.ForeignKeyConstraint(['requirement_id'], ['Requirements.id'], ), 173 | sa.ForeignKeyConstraint(['service_id'], ['Services.id'], ), 174 | sa.PrimaryKeyConstraint('id') 175 | ) 176 | # ### end Alembic commands ### 177 | 178 | 179 | def downgrade(): 180 | # ### commands auto generated by Alembic - please adjust! ### 181 | op.drop_table('ServiceRequirements') 182 | op.drop_table('Reviews') 183 | op.drop_table('Services') 184 | op.drop_table('WebPackages') 185 | op.drop_table('UserLanguages') 186 | op.drop_table('Skills') 187 | op.drop_table('ReqAnswers') 188 | op.drop_table('Educations') 189 | op.drop_table('BugPackages') 190 | op.drop_table('Webs') 191 | op.drop_table('Users') 192 | op.drop_table('ServiceLanguages') 193 | op.drop_table('Requirements') 194 | op.drop_table('Languages') 195 | op.drop_table('Categories') 196 | op.drop_table('Bugs') 197 | # ### end Alembic commands ### 198 | -------------------------------------------------------------------------------- /react-app/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_BASE_URL=http://localhost:5000 2 | -------------------------------------------------------------------------------- /react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /react-app/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | Your React App will live here. While is development, run this application from this location using `npm start`. 4 | 5 | 6 | No environment variables are needed to run this application in development, but be sure to set the REACT_APP_BASE_URL environment variable in heroku! 7 | 8 | This app will be automatically built when you deploy to heroku, please see the `heroku-postbuild` script in your `express.js` applications `package.json` to see how this works. 9 | -------------------------------------------------------------------------------- /react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "http-proxy-middleware": "^1.0.5", 10 | "react": "^17.0.0", 11 | "react-dom": "^17.0.0", 12 | "react-redux": "^7.2.4", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "3.4.3", 15 | "redux": "^4.1.0", 16 | "redux-logger": "^3.0.6", 17 | "redux-thunk": "^2.3.0", 18 | "socket.io-client": "^4.1.2" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": "react-app" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "proxy": "http://localhost:5000" 42 | } 43 | -------------------------------------------------------------------------------- /react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixzzels/Programmerr/40c9d563e3a2187d4621c1b58bbd07e06aac5640/react-app/public/favicon.ico -------------------------------------------------------------------------------- /react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | Programmerr 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /react-app/public/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixzzels/Programmerr/40c9d563e3a2187d4621c1b58bbd07e06aac5640/react-app/public/splash.png -------------------------------------------------------------------------------- /react-app/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { BrowserRouter, Route, Switch } from "react-router-dom"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | // import LoginForm from "./components/auth/LoginForm"; 5 | // import SignUpForm from "./components/auth/SignUpForm"; 6 | // import NavBar from "./components/NavBar"; 7 | import ProtectedRoute from "./components/auth/ProtectedRoute"; 8 | // import UsersList from "./components/UsersList"; 9 | // import User from "./components/User"; 10 | import { authenticate } from "./store/session"; 11 | import SplashPage from "./components/SplashPage"; 12 | import HomePage from "./components/HomePage"; 13 | import ProfilePage from "./components/ProfilePage"; 14 | import PublicProfile from "./components/PublicProfile"; 15 | import CategoryPage from "./components/CategoryPage"; 16 | import ServicePage from "./components/ServicePage"; 17 | import NewService from "./components/NewService"; 18 | import EditService from "./components/EditService"; 19 | import PageNotFound from "./components/PageNotFound"; 20 | import Footer from "./components/Footer"; 21 | 22 | 23 | 24 | function App() { 25 | const user = useSelector(state => state.session.user) 26 | const [loaded, setLoaded] = useState(false); 27 | const dispatch = useDispatch(); 28 | 29 | useEffect(() => { 30 | (async () => { 31 | await dispatch(authenticate()); 32 | setLoaded(true); 33 | })(); 34 | }, []); 35 | 36 | if (!loaded) { 37 | return null; 38 | } 39 | 40 | return ( 41 | 42 | {/* */} 43 | 44 | 45 | {user ? : } 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {/* 79 |

My Home Page

80 |
*/} 81 |
82 |