def goal_completed(mission_id): """Endpoint to add/remove business from a mission's goals_completed.""" if not g.user: return Unauthorized() user_mission = UserMission.query.filter_by( user_id=g.user.id, mission_id=mission_id).one() goals_completed = user_mission.goals_completed.copy() data = request.json if data['business_id'] in goals_completed: goals_completed.remove(data['business_id']) out = {'success': 'Goal Open!'} else: goals_completed.append(data['business_id']) out = {'success': 'Goal Completed!'} user_mission.goals_completed = goals_completed try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': 'Error!'}) return jsonify(out)
def remove_from_mission(mission_id): """Endpoint to remove business from mission .""" mission = Mission.query.get_or_404(mission_id) if not g.user and g.user.id == mission.editor: return Unauthorized() data = request.json business = Business.query.get_or_404(data['business_id']) if business not in mission.businesses: return jsonify({'success': 'Business not in mission.'}) mission.businesses.remove(business) # don't allow sharing when mission has no businesses. if len(mission.businesses) == 0: mission.is_public = False try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': 'Error!'}) return jsonify({'success': 'Business Removed from Mission!'})
def delete_report(report_id): """Delete report view.""" report = Report.query.get_or_404(report_id) if not report.user_id == g.user.id: return Unauthorized() old_file = report.photo_file if report.photo_file else None db.session.delete(report) try: db.session.commit() if old_file: H.s3_delete(old_file) flash("Report Deleted!", 'success') except Exception as e: db.session.rollback() flash("Error Deleting Report!", 'danger') H.error_logging(e) return redirect(url_for('user_views.user_detail', username=g.user.username))
def add_to_mission(mission_id): """Add business to mission endpoint.""" if not g.user: return Unauthorized() mission = Mission.query.get_or_404(mission_id) data = request.json business = Business.query.get(data['id']) if business: if business in mission.businesses: return jsonify({'success': 'Already Added.', 'color': 'warning'}) else: # Index page adds new businesses to DB. business = Business.create( id=data['id'], name=data['name'], city=data['city'], state=data['state'], country=data['country'], longitude=float(data['longitude'] or 0), latitude=float(data['latitude'] or 0)) mission.businesses.append(business) try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': 'Error!'}) return jsonify({'success': 'Added!', 'color': 'green'})
def add_business(): """Add business to database if it is not present.""" data = request.json business = Business.query.get(data['id']) if business: return jsonify({'success': 'business already in database'}) business = Business.create(id=data['id'], name=data['name'], city=data['city'], state=data['state'], country=data['country'], longitude=float(data['longitude'] or 0), latitude=float(data['latitude'] or 0)) try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': 'Error!'}) return jsonify({'success': 'Added!'})
def submit_feedback(): """Endpoint for user to submit feedback to be emailed to developer.""" data = request.json feedback = data['feedback'] email = data.get('email', '') user = g.user.username if g.user else 'Anonymous' receiver = "*****@*****.**" body = f"<h5>Feedback:</h5>{feedback}<p>From: {user} --Email: {email}</p>" try: requests.post(f"https://api.mailgun.net/v3/{MAILGUN_DOMAIN}/messages", auth=("api", MAILGUN_API_KEY), data={ "from": f"Feedback <mailgun@{MAILGUN_DOMAIN}>", "to": [receiver], "subject": "Feedback", "html": body }) except Exception as e: H.error_logging(e) return jsonify({'error': 'Error sending message', 'color': 'warning'}) return jsonify({'success': 'Feedback Received!', 'color': 'green'})
def set_prefrences(): """Endpoint to change user preferences. This is called by two different forms by two different event handlers. The Boolean form has checkbox data and updates preferences on onChange events. The preferences-text form updates when user selects official address.""" if not g.user: return Unauthorized() data = request.json preferences = g.user.preferences.__dict__.copy() # If onChange update of Boolean preferences if data.get('Boolean'): # set each setting to true if data is present else False for key in BOOLEAN_PREFERENCES: preferences[key] = bool(data.get(key, False)) else: preferences['home_address'] = data.get('home_address_official') if data.get('home_coords'): preferences['home_coords'] = [ float(x) for x in data['home_coords'].split(',') ] g.user.preferences = SimpleNamespace(**preferences) try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'feedback': 'Error!'}) return jsonify({'feedback': 'Updated'})
def update_mission(): """Endpoint to update a mission.""" note = {} data = request.json mission = Mission.query.get_or_404(data['id']) if not g.user.id == mission.editor: return Unauthorized() if data.get('is_public'): if len(mission.businesses): mission.share() else: note['note'] = 'You cannot share a mission without goals.' del data['is_public'] else: data['is_public'] = False mission.update(**data) try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': repr(e)}) return jsonify({ 'success': 'updated', 'mission': mission.serialize(), **note })
def like_mission(mission_id): """Endpoint to like and un-like missions.""" if not g.user: return Unauthorized() mission = Mission.query.get_or_404(mission_id) likes = mission.likes.copy() if g.user.id in likes: likes.remove(g.user.id) success = 'removed' else: likes.add(g.user.id) success = 'added' mission.likes = likes try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'feedback': 'Error!'}) return jsonify({'success': success, 'likes': len(mission.likes)})
def delete_mission(mission_id): """Endpoint to delete a mission.""" if not g.user: return Unauthorized() mission = Mission.query.get_or_404(mission_id) g.user.missions.remove(mission) db.session.commit() # if mission was shared and is in use or a report was written don't delete. if (mission.date_shared and mission.user_missions) or mission.reports: mission.is_public = False mission.editor = 2 db.session.commit() return jsonify({'success': 'Mission Deleted!'}) try: db.session.delete(mission) db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': repr(e)}) return jsonify({'success': 'Mission Deleted!'})
def check_google_token(): """Endpoint to check validity of google user token and sign user in, or create new account for user. Return success message for successful user creation or sign-in.""" data = request.json user_token = data['idtoken'] try: idinfo = id_token.verify_oauth2_token(user_token, requests_google.Request(), GOOGLE_O_AUTH_CLIENT_ID) except ValueError as e: H.error_logging(e) return jsonify({'error': f'Autorization error: {e}'}) # ID token is valid. Get the user's Google Account ID. google_id = idinfo['sub'] user = User.query.filter_by(password=google_id).first() # If not user with this google id try to create one. if not user: email = idinfo['email'] username = idinfo['name'] image_url = idinfo['picture'] # If email already in use user should sign in using email and password. if User.query.filter_by(email=email).first(): return jsonify({ 'error': 'Email already in use. Please log in using email and password.' # noqa e501 }) # If username taken add nonce until valid username is formed. if User.query.filter_by(username=username).first(): nonce = 1 username = f'{username}{nonce}' while User.query.filter_by(username=username).first(): nonce += 1 username = f'{username}{nonce}' try: user = User(email=email, username=username, password=google_id, avatar_url=image_url) db.session.add(user) db.session.commit() user.add_bookmarks() except Exception as e: H.error_logging(e) return jsonify({'error': f'Error creating new user: {e}'}) session['user_id'] = user.id flash(f"Welcome {user.username}!", "success") return jsonify({'success': 'User logged in.'})
def search_yelp(): """API endpoint to relay search to Yelp search.""" headers = {'Authorization': f'Bearer {YELP_API_KEY}'} params = parse_query_params(request.args) try: res = requests.get(f'{YELP_URL}/businesses/search', params=params, headers=headers) except Exception as e: H.error_logging(e) return jsonify({'error': {'message': repr(e)}}) return res.json()
def login(): """Login view.""" if g.user: return redirect(url_for('.user_detail', username=g.user.username)) form = LoginForm() if form.validate_on_submit(): email = form.email.data password = form.password.data user = User.authenticate(email, password) if user is None: form.email.errors.append("Email not found.") elif user is False: form.password.errors.append("Password incorrect.") else: session['user_id'] = user.id session.permanent = True flash(f"Welcome {user.username}!", 'success') return H.next_page_logic(request) if request.method == 'POST': flash("Please fix all form errors.", "warning") # Create URL for signin button that passes all URL data. signup_url = request.full_path.replace('login', 'signup') return render_template('user_views/login.html', form=form, signup_url=signup_url)
def create_mission(): """Endpoint to create a mission.""" if not g.user: return Unauthorized() try: mission = Mission.create(editor=g.user.id, **request.json) db.session.commit() g.user.missions.append(mission) db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': repr(e)}) return jsonify({'success': 'Mission Created!', 'mission': mission.serialize()})
def business_details(business_id): """API endpoint to get Yelp business details, Yelp business reviews, Foursquare venue id, and Foursquare business details.""" headers = {'Authorization': f'Bearer {YELP_API_KEY}'} try: # Get business details res = requests.get(f'{YELP_URL}/businesses/{business_id}', headers=headers) res.raise_for_status() # Get reviews for business res2 = requests.get(f'{YELP_URL}/businesses/{business_id}/reviews', headers=headers) res2.raise_for_status() # Get Foursquare venue id params_venue = foursq_venue_params(request) res3 = requests.get(f'{FOURSQUARE_URL}/venues/search', params=params_venue) except Exception as e: H.error_logging(e) return jsonify({'error': repr(e)}) if res3.ok: # Try to get premium request. try: venue_id = res3.json()['response']['venues'][0]['id'] # Get Foursquare business details res4 = requests.get(f'{FOURSQUARE_URL}/venues/{venue_id}', params=foursq_params()) # If over limit our data response won't contain foursquare data. except Exception as e: H.error_logging(e) data = res.json() data['reviews'] = res2.json()['reviews'] # Add gastronaut reports for this business if any. data = add_reports_data(business_id, data) # Add foursquare data if any. if res3.ok and res4.ok: data = add_foursquare_data(data, res4.json()) return data
def add_mission(mission_id): """Endpoint to add mission to user's missions.""" if not g.user: return Unauthorized() mission = Mission.query.get_or_404(mission_id) if mission in g.user.missions: return jsonify({'success': 'Mission Already Added.'}) g.user.missions.append(mission) try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': 'Error!'}) return jsonify({'success': 'Mission Added!'})
def remove_mission(mission_id): """Endpoint to remove mission from user's missions.""" if not g.user: return Unauthorized() mission = Mission.query.get_or_404(mission_id) if mission not in g.user.missions: return jsonify({'success': 'Mission Already Removed.'}) g.user.missions.remove(mission) try: db.session.commit() except Exception as e: H.error_logging(e) return jsonify({'error': 'Error!'}) return jsonify({'success': 'Mission Removed!'})
def index(): """Home view.""" search_term = request.args.get('q') lat, lng = H.get_coords_from_IP_address(request) return render_template('main_views/index.html', YELP_CATEGORIES=get_yelp_categories(), first_letters=first_letters, lat=lat, lng=lng, search_term=search_term)
def user_edit(): """User edit profile view.""" form = EditUserForm(obj=g.user) if form.validate_on_submit(): form.populate_obj(g.user) try: db.session.commit() flash("Profile Updated!", "success") return redirect(url_for('.user_detail', username=g.user.username)) except Exception as e: db.session.rollback() flash("Error Updating User Profile", 'danger') H.error_logging(e) if request.method == 'POST': flash("Please fix all form errors.", "warning") return render_template('user_views/edit_user.html', form=form)
def signup(): """Sign up view.""" if g.user: return redirect(url_for('.user_detail', username=g.user.username)) form = AddUserForm() if form.validate_on_submit(): password = form.password.data # Collect relevant form data items to a dictionary. relevant_data = { k: v for k, v in form.data.items() if k in User.set_get() } try: new_user = User.register(password=password, **relevant_data) session['user_id'] = new_user.id session.permanent = True flash(f"Welcome {new_user.username}!", "success") return H.next_page_logic(request) except Exception as e: db.session.rollback() flash("Error Creating User", 'danger') H.error_logging(e) if request.method == 'POST': flash("Please fix all form errors.", "warning") # Create URL for login button that passes all URL data. login_url = request.full_path.replace("/signup", "/login") return render_template('user_views/signup.html', form=form, login_url=login_url)
def navbar_search(): """View to route navbar searches. If normal term search on index page. If @username check for user and route to user detail if found. If username not found return to page user searched from.""" search_term = request.args.get('q') if search_term and search_term.startswith('@'): search_user = User.query.filter_by(username=search_term[1:]).first() if not search_user: search_user = User.query.filter( User.username.ilike(f'%{search_term[1:]}%')).first() if search_user: return redirect( url_for('user_views.user_detail', username=search_user.username)) flash(f'Gastronaut {search_term} not found', 'warning') return H.next_page_logic(request) return redirect(url_for('.index', q=search_term))
def edit_report(report_id): """Report edit view.""" report = Report.query.get_or_404(report_id) form = EditReportForm(obj=report) # Check if file was cleared and remove from form to pass validation. old_file = H.check_for_clear_file(form) if form.validate_on_submit(): # File upload handling logic. form, f, path, old_file = H.check_file_upload_logic_w_clear( form, old_file) form.populate_obj(report) try: db.session.commit() if f: S3_CLIENT.upload_fileobj(f, S3_BUCKET_NAME, path) if old_file: H.s3_delete(old_file) flash("Report Edited!", 'success') return redirect(url_for('.report_detail', report_id=report.id)) except Exception as e: db.session.rollback() flash("Error Editing Report!", 'danger') H.error_logging(e) if report.mission_id: model = Mission.query.get_or_404(report.mission_id) kind = 'Mission' else: model = Business.query.get_or_404(report.business_id) kind = 'Business' if request.method == 'POST': flash("Please fix all form errors.", "warning") return render_template("reports_crud/edit_report.html", form=form, model=model, kind=kind, report_id=report_id)
def logout(): """User logout view.""" del session['user_id'] flash(f"{g.user.username} logged out.", 'success') return H.next_page_logic(request)
def unauthorized(e): return H.render_template("401.html"), 401
def add_report(): """Write Report View.""" mission_id = request.args.get('mission_id') business_id = request.args.get('business_id') if mission_id and business_id: return BadRequest form = AddReportForm() if form.validate_on_submit(): relevant_data = { k: v for k, v in form.data.items() if k in Report.set_get() } form, relevant_data, f, path = H.check_file_upload_logic( form, relevant_data) report = Report.create(user_id=g.user.id, mission_id=mission_id, business_id=business_id, **relevant_data) try: db.session.commit() if f: S3_CLIENT.upload_fileobj(f, S3_BUCKET_NAME, path) flash("Report added!", 'success') return redirect(url_for('.report_detail', report_id=report.id)) except Exception as e: db.session.rollback() flash("Error Creating Report!", 'danger') H.error_logging(e) existing_report = H.check_for_existing_report(mission_id, business_id) if existing_report: # redirect to edit_report view for this report and relay request args. request_ars = request.args.to_dict() return redirect( url_for('.edit_report', report_id=existing_report.id, **request_ars)) if mission_id: kind = 'Mission' model = Mission.query.get_or_404(mission_id) else: kind = 'Business' model = Business.query.get(business_id) if not model: model = H.add_new_business(business_id, request.args) if model is False: return BadRequest if request.method == 'POST': flash("Please fix all form errors.", "warning") return render_template("reports_crud/add_report.html", form=form, model=model, kind=kind)
def page_not_found(e): return H.render_template("404.html"), 404