Feat: Multiplayer sessions added using CRUD database.
This commit is contained in:
0
controllers/__init__.py
Normal file
0
controllers/__init__.py
Normal file
0
controllers/core/__init__.py
Normal file
0
controllers/core/__init__.py
Normal file
0
controllers/legal/__init__.py
Normal file
0
controllers/legal/__init__.py
Normal file
78
controllers/legal/legal.py
Normal file
78
controllers/legal/legal.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
Project: PARTS Website
|
||||
Author: Edward Middleton-Smith
|
||||
Precision And Research Technology Systems Limited
|
||||
|
||||
Technology: App Routing
|
||||
Feature: Legal Routes
|
||||
|
||||
Description:
|
||||
Legal Section Controller.
|
||||
"""
|
||||
|
||||
# IMPORTS
|
||||
# internal
|
||||
# from models.model_view_home import Model_View_Home
|
||||
from models.model_view_license import Model_View_License
|
||||
from models.model_view_privacy_policy import Model_View_Privacy_Policy
|
||||
from models.model_view_accessibility_report import Model_View_Accessibility_Report
|
||||
from models.model_view_accessibility_statement import Model_View_Accessibility_Statement
|
||||
from models.model_view_retention_schedule import Model_View_Retention_Schedule
|
||||
import lib.argument_validation as av
|
||||
# external
|
||||
from flask import render_template, Blueprint, send_from_directory
|
||||
|
||||
|
||||
routes_legal = Blueprint('routes_legal', __name__)
|
||||
|
||||
|
||||
# snore
|
||||
@routes_legal.route('/license', methods=['GET'])
|
||||
def license():
|
||||
try:
|
||||
model = Model_View_License()
|
||||
html_body = render_template('pages/legal/_license.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
@routes_legal.route('/accessibility-statement', methods=['GET'])
|
||||
def accessibility_statement():
|
||||
try:
|
||||
model = Model_View_Accessibility_Statement()
|
||||
html_body = render_template('pages/legal/_accessibility_statement.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
@routes_legal.route('/accessibility-report', methods=['GET'])
|
||||
def accessibility_report():
|
||||
try:
|
||||
model = Model_View_Accessibility_Report()
|
||||
html_body = render_template('pages/legal/_accessibility_report.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
@routes_legal.route('/retention-schedule', methods=['GET'])
|
||||
def retention_schedule():
|
||||
try:
|
||||
model = Model_View_Retention_Schedule()
|
||||
html_body = render_template('pages/legal/_retention_schedule.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
@routes_legal.route('/privacy-policy', methods=['GET'])
|
||||
def privacy_policy():
|
||||
try:
|
||||
model = Model_View_Privacy_Policy()
|
||||
html_body = render_template('pages/legal/_privacy_policy.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
@routes_legal.route('/robots.txt', methods=['GET'])
|
||||
def robots_txt():
|
||||
return send_from_directory('static', 'docs/robots.txt')
|
||||
@routes_legal.route('/favicon.ico', methods=['GET'])
|
||||
def favicon_ico():
|
||||
return send_from_directory('static', 'images/logo.ico', mimetype='image/vnd.microsoft.icon') # -and-company-name-curved-0.5
|
||||
@routes_legal.route('/sitemap.xml', methods=['GET'])
|
||||
def sitemap_xml():
|
||||
return send_from_directory('static', 'docs/sitemap.xml')
|
||||
0
controllers/tcg/__init__.py
Normal file
0
controllers/tcg/__init__.py
Normal file
378
controllers/tcg/mtg_game.py
Normal file
378
controllers/tcg/mtg_game.py
Normal file
@@ -0,0 +1,378 @@
|
||||
"""
|
||||
Project: PARTS Website
|
||||
Author: Edward Middleton-Smith
|
||||
Precision And Research Technology Systems Limited
|
||||
|
||||
Technology: App Routing
|
||||
Feature: TCG - MTG Game Routes
|
||||
|
||||
Description:
|
||||
MTG Game Page Controller.
|
||||
"""
|
||||
|
||||
# IMPORTS
|
||||
# internal
|
||||
from business_objects.api import API
|
||||
from business_objects.tcg.mtg_game import MTG_Game
|
||||
from business_objects.tcg.mtg_game_player import MTG_Game_Player, Parameters_MTG_Game_Player
|
||||
from business_objects.tcg.mtg_game_round import MTG_Game_Round, Parameters_MTG_Game_Round
|
||||
from business_objects.tcg.mtg_game_round_player_damage import MTG_Game_Round_Player_Damage, Parameters_MTG_Game_Round_Player_Damage
|
||||
from business_objects.tcg.mtg_deck import MTG_Deck, Parameters_MTG_Deck
|
||||
from business_objects.tcg.user import User, Parameters_User
|
||||
from datastores.datastore_mtg import DataStore_MTG
|
||||
from datastores.datastore_user import DataStore_User
|
||||
from forms.tcg.game import Filters_MTG_Game
|
||||
from helpers.helper_app import Helper_App
|
||||
from models.model_view_mtg_base import Model_View_MTG_Base
|
||||
from models.model_view_mtg_game import Model_View_MTG_Game
|
||||
from models.model_view_mtg_games import Model_View_MTG_Games
|
||||
from models.model_view_mtg_home import Model_View_MTG_Home
|
||||
import lib.argument_validation as av
|
||||
# external
|
||||
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, Blueprint, current_app, flash, Response
|
||||
from flask_mail import Mail, Message
|
||||
from extensions import db, oauth, mail
|
||||
from urllib.parse import quote_plus, urlencode
|
||||
from authlib.integrations.flask_client import OAuth
|
||||
from authlib.integrations.base_client import OAuthError
|
||||
from urllib.parse import quote, urlparse, parse_qs
|
||||
import json
|
||||
import base64
|
||||
import hmac
|
||||
import hashlib
|
||||
import datetime
|
||||
|
||||
|
||||
routes_mtg_game = Blueprint('routes_mtg_game', __name__)
|
||||
|
||||
|
||||
@routes_mtg_game.route(Model_View_MTG_Base.HASH_PAGE_MTG_GAMES, methods=['GET'])
|
||||
def games():
|
||||
Helper_App.console_log('mtg games')
|
||||
Helper_App.console_log(f'request_args: {request.args}')
|
||||
try:
|
||||
form_filters = Filters_MTG_Game.from_json(request.args)
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f'Error: {e}')
|
||||
form_filters = Filters_MTG_Game()
|
||||
Helper_App.console_log(f'form_filters={form_filters}')
|
||||
|
||||
datastore = DataStore_MTG()
|
||||
user_session = datastore.get_user_session()
|
||||
if not user_session.get_is_logged_in():
|
||||
return redirect(url_for('routes_mtg_game.home'))
|
||||
|
||||
parameters_game = form_filters.to_parameters(user_id_session = user_session.user_id)
|
||||
|
||||
model = Model_View_MTG_Games(parameters_game = parameters_game)
|
||||
model.form_filters = form_filters
|
||||
Helper_App.console_log(f'form_filters={form_filters}')
|
||||
return render_template('pages/tcg/mtg/_games.html', model=model)
|
||||
|
||||
|
||||
@routes_mtg_game.route(f'{Model_View_MTG_Base.HASH_PAGE_MTG_GAME}/<int:game_id>', methods=['GET'])
|
||||
def game_detail(game_id):
|
||||
Helper_App.console_log(f'mtg game detail: {game_id}')
|
||||
model = Model_View_MTG_Game(game_id=game_id)
|
||||
if not model.is_user_logged_in:
|
||||
return redirect(url_for('routes_mtg_game.home'))
|
||||
if model.game is None:
|
||||
flash('Game not found', 'error')
|
||||
return redirect(url_for('routes_mtg_game.games'))
|
||||
return render_template('pages/tcg/mtg/_game.html', model=model)
|
||||
|
||||
|
||||
@routes_mtg_game.route(Model_View_MTG_Base.HASH_SAVE_MTG_GAME, methods=['POST'])
|
||||
def save_game():
|
||||
_m = 'routes_mtg_game.save_game'
|
||||
data = Helper_App.get_request_data(request)
|
||||
Helper_App.console_log(f'{_m}\n{data}')
|
||||
try:
|
||||
"""
|
||||
form_filters = Filters_MTG_Game.from_json(data.get(Model_View_MTG_Base.FLAG_FORM_FILTERS, {}))
|
||||
if not form_filters.validate_on_submit():
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
|
||||
})
|
||||
form_filters = Filters_MTG_Game.get_default()
|
||||
"""
|
||||
|
||||
datastore = DataStore_MTG()
|
||||
user_session = datastore.get_user_session()
|
||||
if not user_session.get_is_logged_in():
|
||||
raise Exception('User not logged in')
|
||||
|
||||
games = data.get(Model_View_MTG_Base.FLAG_GAME, [])
|
||||
if len(games) == 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'No games to save.'
|
||||
})
|
||||
Helper_App.console_log(f"Games to save: {games}")
|
||||
|
||||
obj_game = MTG_Game.from_json(games[0])
|
||||
Helper_App.console_log(f'obj_game={obj_game}')
|
||||
|
||||
game_id, errors = DataStore_MTG.save_mtg_game(obj_game)
|
||||
|
||||
if len(errors) > 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error saving games.\n{Model_View_MTG_Game.convert_list_objects_to_json(errors)}'
|
||||
})
|
||||
"""
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_SUCCESS,
|
||||
Model_View_MTG_Base.FLAG_DATA: Model_View_MTG_Base.convert_list_objects_to_json(model_return.games)
|
||||
})
|
||||
"""
|
||||
return redirect(url_for('routes_mtg_game.game_detail', game_id = game_id))
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
|
||||
})
|
||||
|
||||
|
||||
@routes_mtg_game.route(Model_View_MTG_Base.HASH_PAGE_MTG_HOME, methods=['GET'])
|
||||
def home():
|
||||
model = Model_View_MTG_Home()
|
||||
if model.user.get_is_logged_in():
|
||||
return redirect(url_for('routes_mtg_game.games'))
|
||||
return render_template('pages/tcg/mtg/_home.html', model=model)
|
||||
|
||||
|
||||
@routes_mtg_game.route(Model_View_MTG_Base.HASH_PAGE_MTG_TRIAL_GAME, methods=['GET'])
|
||||
def trial_game():
|
||||
return send_from_directory('templates/pages/tcg/mtg', 'trial_game.html')
|
||||
|
||||
|
||||
@routes_mtg_game.route('/mtg/api/game/<int:game_id>/players', methods=['GET'])
|
||||
def get_game_players(game_id):
|
||||
"""Get players for a game with enriched user and deck information."""
|
||||
_m = 'routes_mtg_game.get_game_players'
|
||||
Helper_App.console_log(f'{_m}: game_id={game_id}')
|
||||
try:
|
||||
datastore = DataStore_MTG()
|
||||
datastore_user = DataStore_User()
|
||||
user_session = datastore.get_user_session()
|
||||
|
||||
if not user_session.get_is_logged_in():
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'User not logged in'
|
||||
}), 401
|
||||
|
||||
# Get players for this game
|
||||
parameters_player = Parameters_MTG_Game_Player.get_default(user_session.user_id)
|
||||
parameters_player.game_ids = str(game_id)
|
||||
parameters_player.get_all_game = False
|
||||
players, errors = datastore.get_many_mtg_game_player(parameters_player)
|
||||
|
||||
if len(errors) > 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error fetching players: {errors}'
|
||||
}), 500
|
||||
|
||||
players_json = [p.to_json() for p in players]
|
||||
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_SUCCESS,
|
||||
Model_View_MTG_Base.FLAG_DATA: players_json
|
||||
})
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f'{_m} Error: {str(e)}')
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
@routes_mtg_game.route('/mtg/api/game/<int:game_id>/rounds', methods=['GET'])
|
||||
def get_many_game_round(game_id):
|
||||
"""Get rounds for a game."""
|
||||
_m = 'routes_mtg_game.get_many_game_round'
|
||||
Helper_App.console_log(f'{_m}: game_id={game_id}')
|
||||
try:
|
||||
datastore = DataStore_MTG()
|
||||
user_session = datastore.get_user_session()
|
||||
|
||||
if not user_session.get_is_logged_in():
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'User not logged in'
|
||||
}), 401
|
||||
|
||||
# Get rounds for this game
|
||||
parameters_round = Parameters_MTG_Game_Round.get_default(user_session.user_id)
|
||||
parameters_round.game_ids = str(game_id)
|
||||
parameters_round.get_all_game = False
|
||||
rounds, errors = datastore.get_many_mtg_game_round(parameters_round)
|
||||
|
||||
if len(errors) > 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error fetching rounds: {errors}'
|
||||
}), 500
|
||||
|
||||
rounds_json = [r.to_json() for r in rounds]
|
||||
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_SUCCESS,
|
||||
Model_View_MTG_Base.FLAG_DATA: rounds_json
|
||||
})
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f'{_m} Error: {str(e)}')
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
@routes_mtg_game.route('/mtg/api/game/<int:game_id>/damage-records', methods=['GET'])
|
||||
def get_many_game_player_damage(game_id):
|
||||
"""Get damage records for a game."""
|
||||
_m = 'routes_mtg_game.get_many_game_player_damage'
|
||||
Helper_App.console_log(f'{_m}: game_id={game_id}')
|
||||
try:
|
||||
datastore = DataStore_MTG()
|
||||
user_session = datastore.get_user_session()
|
||||
|
||||
if not user_session.get_is_logged_in():
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'User not logged in'
|
||||
}), 401
|
||||
|
||||
parameters_damage = Parameters_MTG_Game_Round_Player_Damage.get_default(user_session.user_id)
|
||||
parameters_damage.game_ids = str(game_id)
|
||||
parameters_damage.get_all_game = False
|
||||
damages, errors = datastore.get_many_mtg_game_round_player_damage(parameters_damage)
|
||||
|
||||
if len(errors) > 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error fetching damage records: {errors}'
|
||||
}), 500
|
||||
|
||||
damage_records = [d.to_json() for d in damages]
|
||||
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_SUCCESS,
|
||||
Model_View_MTG_Base.FLAG_DATA: damage_records
|
||||
})
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f'{_m} Error: {str(e)}')
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
@routes_mtg_game.route(Model_View_MTG_Base.HASH_SAVE_MTG_GAME_PLAYER, methods=['POST'])
|
||||
def save_game_player():
|
||||
_m = 'routes_mtg_game.save_game_player'
|
||||
data = Helper_App.get_request_data(request)
|
||||
Helper_App.console_log(f'{_m}\n{data}')
|
||||
try:
|
||||
"""
|
||||
form_filters = Filters_MTG_Game.from_json(data.get(Model_View_MTG_Base.FLAG_FORM_FILTERS, {}))
|
||||
if not form_filters.validate_on_submit():
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
|
||||
})
|
||||
form_filters = Filters_MTG_Game.get_default()
|
||||
"""
|
||||
|
||||
datastore = DataStore_MTG()
|
||||
user_session = datastore.get_user_session()
|
||||
if not user_session.get_is_logged_in():
|
||||
raise Exception('User not logged in')
|
||||
|
||||
players = data.get(Model_View_MTG_Base.FLAG_PLAYER, [])
|
||||
if len(players) == 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'No players to save.'
|
||||
})
|
||||
|
||||
objs_player = []
|
||||
for player in players:
|
||||
obj_player = MTG_Game_Player.from_json(player)
|
||||
Helper_App.console_log(f'obj_player = {obj_player}')
|
||||
objs_player.append(obj_player)
|
||||
success, errors = DataStore_MTG.save_mtg_game_player(objs_player)
|
||||
|
||||
if len(errors) > 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error saving players.\n{Model_View_MTG_Game.convert_list_objects_to_json(errors)}'
|
||||
})
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_SUCCESS if success else Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_DATA: None
|
||||
})
|
||||
# return redirect(url_for('routes_mtg_game.game_detail', game_id = game_id))
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
|
||||
})
|
||||
|
||||
@routes_mtg_game.route(Model_View_MTG_Base.HASH_SAVE_MTG_GAME_ROUND_PLAYER_DAMAGE, methods=['POST'])
|
||||
def save_game_round_player_damage():
|
||||
_m = 'routes_mtg_game.save_game_round_player_damage'
|
||||
data = Helper_App.get_request_data(request)
|
||||
Helper_App.console_log(f'{_m}\n{data}')
|
||||
try:
|
||||
datastore = DataStore_MTG()
|
||||
user_session = datastore.get_user_session()
|
||||
if not user_session.get_is_logged_in():
|
||||
raise Exception('User not logged in')
|
||||
|
||||
rounds = data.get(Model_View_MTG_Base.FLAG_ROUND, [])
|
||||
if len(rounds) == 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'No rounds to save.'
|
||||
})
|
||||
|
||||
objs_round = []
|
||||
for round in rounds:
|
||||
obj_round = MTG_Game_Round.from_json(round)
|
||||
Helper_App.console_log(f'obj_round = {obj_round}')
|
||||
objs_round.append(obj_round)
|
||||
|
||||
damages = data.get(Model_View_MTG_Base.FLAG_DAMAGE, [])
|
||||
if len(damages) == 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: 'No damages to save.'
|
||||
})
|
||||
|
||||
objs_damage = []
|
||||
for damage in damages:
|
||||
obj_damage = MTG_Game_Round_Player_Damage.from_json(damage)
|
||||
Helper_App.console_log(f'obj_damage = {obj_damage}')
|
||||
objs_damage.append(obj_damage)
|
||||
|
||||
success, errors = DataStore_MTG.save_mtg_game_round_player_damage(objs_round, objs_damage)
|
||||
|
||||
if len(errors) > 0:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Error saving round player damages.\n{Model_View_MTG_Game.convert_list_objects_to_json(errors)}'
|
||||
})
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_SUCCESS if success else Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_DATA: None
|
||||
})
|
||||
# return redirect(url_for('routes_mtg_game.game_detail', game_id = game_id))
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
Model_View_MTG_Base.FLAG_STATUS: Model_View_MTG_Base.FLAG_FAILURE,
|
||||
Model_View_MTG_Base.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
|
||||
})
|
||||
255
controllers/user/user.py
Normal file
255
controllers/user/user.py
Normal file
@@ -0,0 +1,255 @@
|
||||
"""
|
||||
Project: PARTS Website
|
||||
Author: Edward Middleton-Smith
|
||||
Precision And Research Technology Systems Limited
|
||||
|
||||
Technology: App Routing
|
||||
Feature: User Routes
|
||||
|
||||
Description:
|
||||
Initialises the Flask application, sets the configuration based on the environment, and defines two routes (/ and /about) that render templates with the specified titles.
|
||||
"""
|
||||
|
||||
# IMPORTS
|
||||
# internal
|
||||
from models.model_view_base import Model_View_Base
|
||||
from models.model_view_user import Model_View_User
|
||||
from business_objects.tcg.user import User, Parameters_User
|
||||
from datastores.datastore_user import DataStore_User
|
||||
from mtg_commander_life_tracker.forms.tcg.user import Filters_User
|
||||
from helpers.helper_app import Helper_App
|
||||
import lib.argument_validation as av
|
||||
# external
|
||||
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, Blueprint, current_app
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy import exc
|
||||
from flask_wtf.csrf import generate_csrf
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from extensions import oauth # db,
|
||||
from urllib.parse import quote_plus, urlencode
|
||||
from authlib.integrations.flask_client import OAuth
|
||||
from authlib.integrations.base_client import OAuthError
|
||||
from urllib.parse import quote, urlparse, parse_qs
|
||||
from functools import wraps
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
routes_user = Blueprint('routes_user', __name__)
|
||||
|
||||
def handle_db_disconnect(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except exc.OperationalError as e:
|
||||
if "MySQL server has gone away" in str(e):
|
||||
# Close the session and create a new connection
|
||||
db.session.remove()
|
||||
db.session.rollback()
|
||||
# Retry the operation
|
||||
return f(*args, **kwargs)
|
||||
raise
|
||||
return decorated_function
|
||||
|
||||
# User authentication
|
||||
@routes_user.route("/login", methods=['POST', 'OPTIONS']) # required endpoint for Auth0
|
||||
def login():
|
||||
oauth = current_app.extensions['authlib.integrations.flask_client']
|
||||
try:
|
||||
# Helper_App.console_log('login')
|
||||
# Helper_App.console_log(f'method={request.method}')
|
||||
try:
|
||||
data = request.json
|
||||
try:
|
||||
data = request.get_json()
|
||||
except:
|
||||
data = {}
|
||||
except:
|
||||
data = {}
|
||||
# Helper_App.console_log(f'data={data}')
|
||||
hash_callback = data.get(Model_View_Base.FLAG_CALLBACK, Model_View_Base.HASH_PAGE_MTG_HOME)
|
||||
# Helper_App.console_log(f'hash_callback: {hash_callback}')
|
||||
|
||||
uri_redirect = url_for('routes_user.login_callback', _external=True)
|
||||
# Helper_App.console_log(f'redirect uri: {uri_redirect}')
|
||||
|
||||
# Helper_App.console_log(f'Before red')
|
||||
# Helper_App.console_log(f"Registered clients: {list(oauth._clients.keys())}")
|
||||
try:
|
||||
with current_app.app_context():
|
||||
red = oauth.auth0.authorize_redirect(
|
||||
redirect_uri = uri_redirect
|
||||
, state = quote(hash_callback)
|
||||
)
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f"Error: {str(e)}")
|
||||
# Helper_App.console_log(f'redirect: {red}')
|
||||
headers = red.headers['Location']
|
||||
# Helper_App.console_log(f'headers: {headers}')
|
||||
parsed_url = urlparse(headers)
|
||||
query_params = parse_qs(parsed_url.query)
|
||||
Helper_App.console_log(f"""
|
||||
OAuth Authorize Redirect URL:
|
||||
|
||||
Base URL: {parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}
|
||||
{parsed_url}
|
||||
|
||||
Query Parameters: {query_params}
|
||||
""")
|
||||
return jsonify({'Success': True, Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_SUCCESS, f'{Model_View_Base.FLAG_CALLBACK}': headers})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 400
|
||||
|
||||
|
||||
@routes_user.route("/login_callback")
|
||||
@handle_db_disconnect
|
||||
def login_callback():
|
||||
oauth = current_app.extensions['authlib.integrations.flask_client']
|
||||
# Helper_App.console_log('login_callback')
|
||||
try:
|
||||
# Helper_App.console_log(f'Redceived state: {request.args.get("state")}')
|
||||
token = None
|
||||
try:
|
||||
token = oauth.auth0.authorize_access_token()
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f"Error: {str(e)}")
|
||||
session[current_app.config['ID_TOKEN_USER']] = token
|
||||
|
||||
try:
|
||||
hash_callback = token.get('hash_callback')
|
||||
if hash_callback is None:
|
||||
Helper_App.console_log('hash is none')
|
||||
state = request.args.get('state')
|
||||
Helper_App.console_log(f'state: {state}')
|
||||
hash_callback = state
|
||||
Helper_App.console_log(f'hash_callback: {hash_callback}')
|
||||
except:
|
||||
Helper_App.console_log("get hash callback failed")
|
||||
|
||||
error_state = request.args.get(Model_View_User.FLAG_ERROR_OAUTH)
|
||||
has_error = error_state is not None
|
||||
if has_error:
|
||||
error_description = request.args.get(Model_View_User.FLAG_ERROR_DESCRIPTION_OAUTH)
|
||||
error_text = f'Error: {error_state}: {error_description}'
|
||||
Helper_App.console_log(error_text)
|
||||
return redirect(f"{current_app.config['URL_HOST']}{hash_callback}") # login()
|
||||
|
||||
user = User.from_json_auth0(token)
|
||||
Helper_App.console_log(f'user: {user}')
|
||||
"""
|
||||
session[Model_View_Base.FLAG_USER] = user.to_json()
|
||||
Helper_App.console_log(f'user stored on session')
|
||||
"""
|
||||
|
||||
datastore_user = DataStore_User()
|
||||
try:
|
||||
saved_user, errors = datastore_user.login_user(user)
|
||||
if (len(errors) > 0): raise ValueError(f'Database errors: {errors}')
|
||||
Helper_App.console_log('User logged in')
|
||||
Helper_App.console_log(f'user ({str(type(saved_user))}): {saved_user}')
|
||||
Helper_App.console_log(f'user key: {Model_View_Base.FLAG_USER}')
|
||||
saved_user_json = saved_user.to_json()
|
||||
Helper_App.console_log(f'User JSON: {saved_user_json}')
|
||||
session[Model_View_Base.FLAG_USER] = saved_user_json
|
||||
Helper_App.console_log(f'user stored on session')
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f'User not found: {saved_user}\nDatabase query error: {errors}')
|
||||
|
||||
|
||||
Helper_App.console_log(f'user session: {session.get(Model_View_Base.FLAG_USER, "(Key not found)")}')
|
||||
return redirect(f"{current_app.config['URL_HOST']}{hash_callback}")
|
||||
except Exception as e:
|
||||
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Controller error.\n{e}'})
|
||||
|
||||
@routes_user.route("/logout")
|
||||
def logout():
|
||||
session.clear()
|
||||
url_logout = f"https://{current_app.config['DOMAIN_AUTH0']}/v2/logout?" + urlencode(
|
||||
{
|
||||
"returnTo": url_for("routes_user.logout_callback", _external=True),
|
||||
"client_id": current_app.config['ID_AUTH0_CLIENT'],
|
||||
}
|
||||
)
|
||||
Helper_App.console_log(f"Redirecting to {url_logout}")
|
||||
return redirect(url_logout)
|
||||
|
||||
@routes_user.route("/logout_callback")
|
||||
@handle_db_disconnect
|
||||
def logout_callback():
|
||||
return redirect(url_for('routes_mtg_game.home'))
|
||||
|
||||
@routes_user.route(Model_View_User.HASH_PAGE_USER_ACCOUNT)
|
||||
def user():
|
||||
try:
|
||||
user_session = Model_View_User.get_user_session()
|
||||
if not user_session.get_is_logged_in():
|
||||
return redirect(url_for('routes_mtg_game.home'))
|
||||
form_filters = Filters_User.get_default()
|
||||
model = Model_View_User(form_filters_old = form_filters)
|
||||
model._title = model.user.firstname
|
||||
# model.users = [model.user]
|
||||
html_body = render_template('pages/user/_user.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
|
||||
@routes_user.route(Model_View_User.HASH_PAGE_USER_ACCOUNTS)
|
||||
def users():
|
||||
try:
|
||||
Helper_App.console_log(f'request_args: {request.args}')
|
||||
user_session = Model_View_User.get_user_session()
|
||||
if (not user_session.get_is_logged_in()) or (not user_session.can_admin_user):
|
||||
return redirect(url_for('routes_mtg_game.home'))
|
||||
try:
|
||||
form_filters = Filters_User.from_json(request.args)
|
||||
except Exception as e:
|
||||
Helper_App.console_log(f'Error: {e}')
|
||||
form_filters = Filters_User.get_default()
|
||||
model = Model_View_User(form_filters, hash_page_current = Model_View_User.HASH_PAGE_USER_ACCOUNTS)
|
||||
html_body = render_template('pages/user/_users.html', model = model)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return html_body
|
||||
|
||||
|
||||
@routes_user.route(Model_View_User.HASH_SAVE_USER_USER, methods=['POST'])
|
||||
def save_user():
|
||||
data = Helper_App.get_request_data(request)
|
||||
try:
|
||||
form_filters = Filters_User.from_json(data[Model_View_User.FLAG_FORM_FILTERS])
|
||||
if not form_filters.validate_on_submit():
|
||||
return jsonify({
|
||||
Model_View_User.FLAG_STATUS: Model_View_User.FLAG_FAILURE,
|
||||
Model_View_User.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
|
||||
})
|
||||
model_return = Model_View_User(form_filters_old=form_filters)
|
||||
if not model_return.is_user_logged_in:
|
||||
return redirect(url_for('routes_mtg_game.home'))
|
||||
|
||||
users = data[Model_View_User.FLAG_USER]
|
||||
if len(users) == 0:
|
||||
return jsonify({
|
||||
Model_View_User.FLAG_STATUS: Model_View_User.FLAG_FAILURE,
|
||||
Model_View_User.FLAG_MESSAGE: f'No users.'
|
||||
})
|
||||
objs_user = []
|
||||
for user in users:
|
||||
objs_user.append(User.from_json(user))
|
||||
Helper_App.console_log(f'objs_user={objs_user}')
|
||||
errors = DataStore_User.save_users(data.get('comment', 'No comment'), objs_user)
|
||||
|
||||
if (len(errors) > 0):
|
||||
return jsonify({
|
||||
Model_View_User.FLAG_STATUS: Model_View_User.FLAG_FAILURE,
|
||||
Model_View_User.FLAG_MESSAGE: f'Error saving users.\n{model_return.convert_list_objects_to_json(errors)}'
|
||||
})
|
||||
return jsonify({
|
||||
Model_View_User.FLAG_STATUS: Model_View_User.FLAG_SUCCESS,
|
||||
Model_View_User.FLAG_DATA: Model_View_User.convert_list_objects_to_json(model_return.users)
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
Model_View_User.FLAG_STATUS: Model_View_User.FLAG_FAILURE,
|
||||
Model_View_User.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
|
||||
})
|
||||
Reference in New Issue
Block a user