diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d01070 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +env_tcg/ +env_tcg/* \ No newline at end of file diff --git a/backup_20251020.dump b/backup_20251020.dump new file mode 100644 index 0000000..ec6f20c Binary files /dev/null and b/backup_20251020.dump differ diff --git a/docker/.env b/docker/.env new file mode 100644 index 0000000..dffc6b9 --- /dev/null +++ b/docker/.env @@ -0,0 +1,2 @@ +PGADMIN_EMAIL=teddy@shuffleandskirmish.co.uk +PGADMIN_PASSWORD=k9a8f4jucJaFw3 \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..7d1fc0a --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3.8' + +services: + trading_card_games_pgadmin: + image: dpage/pgadmin4:latest + container_name: trading_card_games_pgadmin + restart: unless-stopped + environment: + PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL:-admin@example.com} + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD:-changeme} + PGADMIN_CONFIG_SERVER_MODE: 'True' + PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False' + volumes: + - trading_card_games_pgadmin_data:/var/lib/pgadmin + networks: + - traefik-public + labels: + - "traefik.enable=true" + - "traefik.http.routers.trading-card-games-pgadmin.rule=Host(`pgadmin.mtg.teddy.org.uk`)" + - "traefik.http.routers.trading-card-games-pgadmin.entrypoints=https" + - "traefik.http.routers.trading-card-games-pgadmin.tls=true" + - "traefik.http.routers.trading-card-games-pgadmin.tls.certresolver=le" + - "traefik.http.services.trading-card-games-pgadmin.loadbalancer.server.port=80" + - "traefik.http.routers.trading-card-games-pgadmin-http.rule=Host(`pgadmin.mtg.teddy.org.uk`)" + - "traefik.http.routers.trading-card-games-pgadmin-http.entrypoints=http" + - "traefik.http.routers.trading-card-games-pgadmin-http.middlewares=https-redirect@docker" + expose: + - "80" + +volumes: + trading_card_games_pgadmin_data: + name: trading_card_games_pgadmin_data + +networks: + traefik-public: + external: true \ No newline at end of file diff --git a/docs/TCG Sole Trader Copy - dead.xlsx b/docs/TCG Sole Trader Copy - dead.xlsx new file mode 100644 index 0000000..4d3d716 Binary files /dev/null and b/docs/TCG Sole Trader Copy - dead.xlsx differ diff --git a/docs/TCG Sole Trader Copy.xlsx b/docs/TCG Sole Trader Copy.xlsx new file mode 100644 index 0000000..9adcee2 Binary files /dev/null and b/docs/TCG Sole Trader Copy.xlsx differ diff --git a/python/bulk_upload_mtg_dictionary_xlsx_to_postgresql.py b/python/bulk_upload_mtg_dictionary_xlsx_to_postgresql.py new file mode 100644 index 0000000..f838767 --- /dev/null +++ b/python/bulk_upload_mtg_dictionary_xlsx_to_postgresql.py @@ -0,0 +1,316 @@ +import pandas as pd +import psycopg2 +from psycopg2 import sql +from datetime import datetime +import numpy as np +import sys + +# Database connection configuration +DB_CONFIG = { + 'dbname': 'tcg', + 'user': 'postgres', + 'password': 'lick_pubes', + 'host': 'localhost', + 'port': '5432' +} + +# Version history configuration +DEFAULT_USER_ID = 3 +DEFAULT_CHANGE_SET_ID = 1 + + +def clean_numeric(value): + """Clean numeric values, handling various formats""" + if pd.isna(value) or value == '': + return None + if isinstance(value, str): + # Remove currency symbols, commas, and spaces + value = value.replace('£', '').replace('$', '').replace(',', '').strip() + try: + return float(value) if value else None + except: + return None + +def clean_boolean(value): + """Convert various boolean representations to Python boolean""" + if pd.isna(value): + return False + if isinstance(value, bool): + return value + if isinstance(value, str): + return value.lower() in ['yes', 'true', '1', 'y'] + return bool(value) + +def clean_datetime(value): + """Convert various date formats to datetime""" + if pd.isna(value) or value == '': + return None + try: + return pd.to_datetime(value) # , dayfirst=True) + except: + return None + +def clean_string(value, max_length=None): + """Clean string values""" + if pd.isna(value) or value == '': + return None + value = str(value).strip() + if max_length and len(value) > max_length: + value = value[:max_length] + return value + +def process_spreadsheet(file_path, connection): + """Process the spreadsheet and insert records into database""" + # Read the Excel file + df = pd.read_excel(file_path, sheet_name='MTG Card Dictionary') + + """ + pd.set_option('display.max_columns', None) + print('dataframe') + print(df) + """ + + cursor = connection.cursor() + + inserted_count = 0 + skipped_count = 0 + + for index, row in df.iterrows(): + try: + # print(index, row) + + # Prepare data for insertion + data = { + 'all_parts': clean_string(row['all_parts']), + 'arena_id': clean_numeric(row['arena_id']), + 'artist': clean_string(row['artist']), + 'artist_ids': clean_string(row['artist_ids'], 250), + 'booster': clean_boolean(row['booster']), + 'border_color': clean_string(row['border_color']), + 'card_back_id': clean_string(row['card_back_id'], 250), + 'card_faces': clean_string(row['card_faces']), + 'cardmarket_id': clean_numeric(row['cardmarket_id']), + 'cmc': clean_numeric(row['cmc']), + 'collector_number': clean_string(row['collector_number']), + 'color_identity': clean_string(row['color_identity']), + 'colors': clean_string(row['colors']), + 'content_warning': clean_boolean(row['content_warning']), + 'digital': clean_boolean(row['digital']), + 'edhrec_rank': clean_numeric(row['edhrec_rank']), + 'finishes': clean_string(row['finishes']), + 'flavor_name': clean_string(row['flavor_name']), + 'flavor_text': clean_string(row['flavor_text']), + 'foil': clean_boolean(row['foil']), + 'frame': clean_string(row['frame']), + 'full_art': clean_boolean(row['full_art']), + 'game_changer': clean_boolean(row['game_changer']), + 'games': clean_string(row['games']), + 'hand_modifier': clean_string(row['hand_modifier']), + 'highres_image': clean_boolean(row['highres_image']), + 'id': clean_string(row['id'], 100), + 'image_status': clean_string(row['image_status']), + 'image_uri_art_crop': clean_string(row['image_uri_art_crop']), + 'image_uri_border_crop': clean_string(row['image_uri_border_crop']), + 'image_uri_large': clean_string(row['image_uri_large']), + 'image_uri_normal': clean_string(row['image_uri_normal']), + 'image_uri_png': clean_string(row['image_uri_png']), + 'image_uri_small': clean_string(row['image_uri_small']), + 'keywords': clean_string(row['keywords']), + 'lang': clean_string(row['lang']), + 'layout': clean_string(row['layout']), + 'legal_alchemy': clean_boolean(row['legal_alchemy']), + 'legal_brawl': clean_boolean(row['legal_brawl']), + 'legal_commander': clean_boolean(row['legal_commander']), + 'legal_duel': clean_boolean(row['legal_duel']), + 'legal_future': clean_boolean(row['legal_future']), + 'legal_gladiator': clean_boolean(row['legal_gladiator']), + 'legal_historic': clean_boolean(row['legal_historic']), + 'legal_legacy': clean_boolean(row['legal_legacy']), + 'legal_modern': clean_boolean(row['legal_modern']), + 'legal_oathbreaker': clean_boolean(row['legal_oathbreaker']), + 'legal_oldschool': clean_boolean(row['legal_oldschool']), + 'legal_pauper': clean_boolean(row['legal_pauper']), + 'legal_paupercommander': clean_boolean(row['legal_paupercommander']), + 'legal_penny': clean_boolean(row['legal_penny']), + 'legal_pioneer': clean_boolean(row['legal_pioneer']), + 'legal_predh': clean_boolean(row['legal_predh']), + 'legal_premodern': clean_boolean(row['legal_premodern']), + 'legal_standard': clean_boolean(row['legal_standard']), + 'legal_standardbrawl': clean_boolean(row['legal_standardbrawl']), + 'legal_timeless': clean_boolean(row['legal_timeless']), + 'legal_vintage': clean_boolean(row['legal_vintage']), + 'life_modifier': clean_string(row['life_modifier']), + 'loyalty': clean_string(row['loyalty']), + 'mana_cost': clean_string(row['mana_cost']), + 'mtgo_id': clean_numeric(row['mtgo_id']), + 'multiverse_ids': clean_string(row['multiverse_ids'], 250), + 'name': clean_string(row['name']), + 'nonfoil': clean_boolean(row['nonfoil']), + 'oracle_id': clean_string(row['oracle_id'], 100), + 'oracle_text': clean_string(row['oracle_text']), + 'oversized': clean_boolean(row['oversized']), + 'penny_rank': clean_numeric(row['penny_rank']), + 'power': clean_string(row['power']), + 'preview_date': clean_datetime(row['preview_date']), + 'preview_source': clean_string(row['preview_source']), + 'preview_source_uri': clean_string(row['preview_source_uri']), + 'price_eur': clean_numeric(row['price_eur']), + 'price_eur_foil': clean_numeric(row['price_eur_foil']), + 'price_tix': clean_numeric(row['price_tix']), + 'price_usd': clean_numeric(row['price_usd']), + 'price_usd_etched': clean_numeric(row['price_usd_etched']), + 'price_usd_foil': clean_numeric(row['price_usd_foil']), + 'printed_name': clean_string(row['printed_name']), + 'printed_text': clean_string(row['printed_text']), + 'printed_type_line': clean_string(row['printed_type_line']), + 'prints_search_uri': clean_string(row['prints_search_uri']), + 'produced_mana': clean_string(row['produced_mana']), + 'promo': clean_boolean(row['promo']), + 'purchase_cardhoarder': clean_string(row['purchase_cardhoarder']), + 'purchase_cardmarket': clean_string(row['purchase_cardmarket']), + 'purchase_tcgplayer': clean_string(row['purchase_tcgplayer']), + 'rarity': clean_string(row['rarity']), + 'released_at': clean_datetime(row['released_at']), + 'reprint': clean_boolean(row['reprint']), + 'reserved': clean_boolean(row['reserved']), + 'rulings_uri': clean_string(row['rulings_uri']), + 'scryfall_set_uri': clean_string(row['scryfall_set_uri']), + 'scryfall_uri': clean_string(row['scryfall_uri']), + 'security_stamp': clean_string(row['security_stamp']), + 'set': clean_string(row['set']), + 'set_id': clean_string(row['set_id'], 100), + 'set_name': clean_string(row['set_name']), + 'set_search_uri': clean_string(row['set_search_uri']), + 'set_type': clean_string(row['set_type']), + 'set_uri': clean_string(row['set_uri']), + 'story_spotlight': clean_boolean(row['story_spotlight']), + 'tcgplayer_id': clean_numeric(row['tcgplayer_id']), + 'textless': clean_boolean(row['textless']), + 'toughness': clean_string(row['toughness']), + 'type_line': clean_string(row['type_line']), + 'uri': clean_string(row['uri']), + 'uri_edhrec': clean_string(row['uri_edhrec']), + 'uri_gatherer': clean_string(row['uri_gatherer']), + 'uri_tcgplayer_infinite_articles': clean_string(row['uri_tcgplayer_infinite_articles']), + 'uri_tcgplayer_infinite_decks': clean_string(row['uri_tcgplayer_infinite_decks']), + 'variation': clean_boolean(row['variation']), + 'active': True, + 'created_on': 'NOW()', # You might want to use database function or Python datetime + 'created_by_user_id': DEFAULT_USER_ID, + 'updated_last_on': 'NOW()', + 'updated_last_by_user_id': DEFAULT_USER_ID, + 'change_set_id': DEFAULT_CHANGE_SET_ID + } + + # Build the insert query + insert_query = """ + INSERT INTO tcg.public.TCG_MTG_Card ( + all_parts, arena_id, artist, artist_ids, booster, border_color, card_back_id, + card_faces, cardmarket_id, cmc, collector_number, color_identity, colors, + content_warning, digital, edhrec_rank, finishes, flavor_name, flavor_text, + foil, frame, full_art, game_changer, games, hand_modifier, highres_image, + id, image_status, image_uri_art_crop, image_uri_border_crop, image_uri_large, + image_uri_normal, image_uri_png, image_uri_small, keywords, lang, layout, + legal_alchemy, legal_brawl, legal_commander, legal_duel, legal_future, + legal_gladiator, legal_historic, legal_legacy, legal_modern, legal_oathbreaker, + legal_oldschool, legal_pauper, legal_paupercommander, legal_penny, legal_pioneer, + legal_predh, legal_premodern, legal_standard, legal_standardbrawl, legal_timeless, + legal_vintage, life_modifier, loyalty, mana_cost, mtgo_id, multiverse_ids, + name, nonfoil, oracle_id, oracle_text, oversized, penny_rank, power, + preview_date, preview_source, preview_source_uri, price_eur, price_eur_foil, + price_tix, price_usd, price_usd_etched, price_usd_foil, printed_name, + printed_text, printed_type_line, prints_search_uri, produced_mana, promo, + purchase_cardhoarder, purchase_cardmarket, purchase_tcgplayer, rarity, + released_at, reprint, reserved, rulings_uri, scryfall_set_uri, scryfall_uri, + security_stamp, set, set_id, set_name, set_search_uri, set_type, set_uri, + story_spotlight, tcgplayer_id, textless, toughness, type_line, uri, + uri_edhrec, uri_gatherer, uri_tcgplayer_infinite_articles, + uri_tcgplayer_infinite_decks, variation, active, created_on, created_by_user_id, + updated_last_on, updated_last_by_user_id, change_set_id + ) VALUES ( + %(all_parts)s, %(arena_id)s, %(artist)s, %(artist_ids)s, %(booster)s, %(border_color)s, %(card_back_id)s, + %(card_faces)s, %(cardmarket_id)s, %(cmc)s, %(collector_number)s, %(color_identity)s, %(colors)s, + %(content_warning)s, %(digital)s, %(edhrec_rank)s, %(finishes)s, %(flavor_name)s, %(flavor_text)s, + %(foil)s, %(frame)s, %(full_art)s, %(game_changer)s, %(games)s, %(hand_modifier)s, %(highres_image)s, + %(id)s, %(image_status)s, %(image_uri_art_crop)s, %(image_uri_border_crop)s, %(image_uri_large)s, + %(image_uri_normal)s, %(image_uri_png)s, %(image_uri_small)s, %(keywords)s, %(lang)s, %(layout)s, + %(legal_alchemy)s, %(legal_brawl)s, %(legal_commander)s, %(legal_duel)s, %(legal_future)s, + %(legal_gladiator)s, %(legal_historic)s, %(legal_legacy)s, %(legal_modern)s, %(legal_oathbreaker)s, + %(legal_oldschool)s, %(legal_pauper)s, %(legal_paupercommander)s, %(legal_penny)s, %(legal_pioneer)s, + %(legal_predh)s, %(legal_premodern)s, %(legal_standard)s, %(legal_standardbrawl)s, %(legal_timeless)s, + %(legal_vintage)s, %(life_modifier)s, %(loyalty)s, %(mana_cost)s, %(mtgo_id)s, %(multiverse_ids)s, + %(name)s, %(nonfoil)s, %(oracle_id)s, %(oracle_text)s, %(oversized)s, %(penny_rank)s, %(power)s, + %(preview_date)s, %(preview_source)s, %(preview_source_uri)s, %(price_eur)s, %(price_eur_foil)s, + %(price_tix)s, %(price_usd)s, %(price_usd_etched)s, %(price_usd_foil)s, %(printed_name)s, + %(printed_text)s, %(printed_type_line)s, %(prints_search_uri)s, %(produced_mana)s, %(promo)s, + %(purchase_cardhoarder)s, %(purchase_cardmarket)s, %(purchase_tcgplayer)s, %(rarity)s, + %(released_at)s, %(reprint)s, %(reserved)s, %(rulings_uri)s, %(scryfall_set_uri)s, %(scryfall_uri)s, + %(security_stamp)s, %(set)s, %(set_id)s, %(set_name)s, %(set_search_uri)s, %(set_type)s, %(set_uri)s, + %(story_spotlight)s, %(tcgplayer_id)s, %(textless)s, %(toughness)s, %(type_line)s, %(uri)s, + %(uri_edhrec)s, %(uri_gatherer)s, %(uri_tcgplayer_infinite_articles)s, + %(uri_tcgplayer_infinite_decks)s, %(variation)s, %(active)s, %(created_on)s, %(created_by_user_id)s, + %(updated_last_on)s, %(updated_last_by_user_id)s, %(change_set_id)s + ) + """ + + cursor.execute(insert_query, data) + inserted_count += 1 + + if (index + 1) % 500 == 0: + print(f"Processed {index + 1} rows...") + try: + connection.commit() + except psycopg2.Error as e: + print(f'error: {e}') + connection.rollback() + + + except Exception as e: + print(f"Error processing row {index + 2}: {str(e)}") + skipped_count += 1 + connection.rollback() + continue + + # Final commit + try: + connection.commit() + except psycopg2.Error as e: + print(f'error: {e}') + connection.rollback() + + print(f"\nImport completed:") + print(f"- Records inserted: {inserted_count}") + print(f"- Records skipped: {skipped_count}") + + cursor.close() + +def main(): + """Main function to run the import""" + file_path = '/home/teddy/Downloads/Trading Cards.xlsx' + + # Connect to database + try: + print("Connecting to database...") + connection = psycopg2.connect(**DB_CONFIG) + print("Connected successfully!") + + # Process the spreadsheet + print(f"\nProcessing {file_path}...") + process_spreadsheet(file_path, connection) + + except Exception as e: + print(f"Error: {str(e)}") + sys.exit(1) + finally: + if 'connection' in locals() and connection: + connection.close() + print("\nDatabase connection closed.") + +if __name__ == "__main__": + # Configuration notes: + # 1. Update DB_CONFIG with your actual database credentials + # 2. Set DEFAULT_USER_ID to your user_id in the system + # 3. Ensure TCG_MTG_Card table has records with matching Key IDs + # 4. Ensure TCG_MTG_Finish and TCG_Condition tables have the referenced IDs + + main() \ No newline at end of file diff --git a/python/bulk_upload_mtg_inventory_xlsx_to_postgresql.py b/python/bulk_upload_mtg_inventory_xlsx_to_postgresql.py new file mode 100644 index 0000000..3875e14 --- /dev/null +++ b/python/bulk_upload_mtg_inventory_xlsx_to_postgresql.py @@ -0,0 +1,235 @@ +import pandas as pd +import psycopg2 +from psycopg2 import sql +from datetime import datetime +import numpy as np +import sys + +# Database connection configuration +DB_CONFIG = { + 'dbname': 'tcg', + 'user': 'postgres', + 'password': 'lick_pubes', + 'host': 'localhost', + 'port': '5432' +} + +# Version history configuration +DEFAULT_USER_ID = 3 +DEFAULT_CHANGE_SET_ID = 1 + + +def clean_numeric(value): + """Clean numeric values, handling various formats""" + if pd.isna(value) or value == '': + return None + if isinstance(value, str): + # Remove currency symbols, commas, and spaces + value = value.replace('£', '').replace('$', '').replace(',', '').strip() + try: + return float(value) if value else None + except: + return None + +def clean_boolean(value): + """Convert various boolean representations to Python boolean""" + if pd.isna(value): + return False + if isinstance(value, bool): + return value + if isinstance(value, str): + return value.lower() in ['yes', 'true', '1', 'y'] + return bool(value) + +def clean_datetime(value): + """Convert various date formats to datetime""" + if pd.isna(value) or value == '': + return None + try: + return pd.to_datetime(value, dayfirst=True) + except: + return None + +def clean_string(value, max_length=None): + """Clean string values""" + if pd.isna(value) or value == '': + return None + value = str(value).strip() + if max_length and len(value) > max_length: + value = value[:max_length] + return value + +""" +def get_or_create_card_id(cursor, key_id): + "" "Get card_id from TCG_MTG_Card table based on key_id, or create if needed"" " + # First check if card exists + cursor.execute("" " + SELECT card_id FROM tcg.public.TCG_MTG_Card + WHERE key_id = %s AND active = TRUE + "" ", (key_id,)) + + result = cursor.fetchone() + if result: + return result[0] + else: + # You might need to adjust this based on your card creation logic + print(f"Warning: No card found for key_id {key_id}. You may need to create the card record first.") + return None +""" + +def process_spreadsheet(file_path, connection, minimum_row_number = 1): + """Process the spreadsheet and insert records into database""" + # Read the Excel file + df = pd.read_excel(file_path, sheet_name='MTG Cards') + + pd.set_option('display.max_columns', None) + print('dataframe') + print(df) + + cursor = connection.cursor() + + inserted_count = 0 + skipped_count = 0 + + for index, row in df.iterrows(): + if (index + 1 < minimum_row_number): + continue + if (clean_string(row['Name']) == 'Double-Faced Substitute Card'): + continue + try: + # print(index, row) + + # Prepare data for insertion + data = { + 'finish_id': int(row['Finish Id']) # if pd.notna(row['Finish Id']) else 1, # Default to 1 if missing + , 'condition_id': int(row['Condition Id']) # if pd.notna(row['Condition Id']) else 1, # Default to 1 if missing + , 'sleeve_colour_name': clean_string(row['Sleeve'], 250) + , 'location_name': clean_string(row['Location'], 250) + , 'acquired_from': clean_string(row['Acquired From'], 500) + , 'acquired_on': clean_datetime(row['Acquired On']) + , 'cost_gbp': clean_numeric(row['Cost']) # or 0.00, # Default to 0 if missing + , 'sale_price_gbp': clean_numeric(row['Sale Price']) + , 'is_sold': clean_boolean(row['Sold?']) + , 'is_destroyed': clean_boolean(row['Destroyed?']) + , 'notes': clean_string(row['Notes']) + , 'alterations_customisations': clean_string(row['Alterations / Customisations'], 250) + , 'grading_company_name': clean_string(row['Grading Company'], 250) + , 'grading_score': clean_string(row['Grading Score'], 250) + , 'subgrades': clean_string(row['Subgrades'], 250) + , 'misprint_errors': clean_string(row['Misprint Errors'], 250) + , 'miscut_errors': clean_string(row['Miscut Status'], 250) + , 'playability': clean_string(row['Playability'], 250) + , 'owner_user_id': DEFAULT_USER_ID # Using configured user ID + , 'ownership_status_name': clean_string(row['Ownership Status'], 250) + , 'trading_status_name': clean_string(row['Trading Status'], 250) + , 'loaned_to_user_id': None # You'll need to map "Loaned To" to user_id if needed + , 'loan_start_on': clean_datetime(row['Loan Start Date']) + , 'loan_end_on': clean_datetime(row['Loan End Date']) + , 'provenance': clean_string(row['Provenance - e.g. special history, tournaments used in']) + , 'signed_by_names': clean_string(row['Signed By']) + , 'signature_condition_name': clean_string(row['Signature Condition'], 250) + , 'active': True + , 'created_by_user_id': DEFAULT_USER_ID + , 'updated_last_by_user_id': DEFAULT_USER_ID + , 'change_set_id': DEFAULT_CHANGE_SET_ID + + , 'set_code': clean_string(row['Set Code']) + , 'collector_number': clean_string(row['Collector Number']) + , 'display_order': int(row['ID']) + , 'name': clean_string(row['Name']) + , 'is_token': clean_boolean(row['Is Token?']) + , 'token_rear_side_name': clean_string(row['Token Rear Side Card Name']) + , 'token_rear_side_set_code': clean_string(row['Token Rear Side Card Set']) + , 'token_rear_side_collector_number': clean_string(row['Token Rear Side Card Collector Number']) + } + + # Build the insert query + insert_query = """ + INSERT INTO tcg.public.TCG_MTG_Inventory_Temp ( + finish_id, condition_id, sleeve_colour_name, location_name, + acquired_from, acquired_on, cost_gbp, sale_price_gbp, is_sold, + is_destroyed, notes, alterations_customisations, grading_company_name, + grading_score, subgrades, misprint_errors, miscut_errors, playability, + owner_user_id, ownership_status_name, trading_status_name, loaned_to_user_id, + loan_start_on, loan_end_on, provenance, signed_by_names, + signature_condition_name, active, created_by_user_id, + updated_last_by_user_id, change_set_id, + set_code, collector_number, display_order, name, + is_token, token_rear_side_name, token_rear_side_set_code, token_rear_side_collector_number + ) VALUES ( + %(finish_id)s, %(condition_id)s, %(sleeve_colour_name)s, %(location_name)s, + %(acquired_from)s, %(acquired_on)s, %(cost_gbp)s, %(sale_price_gbp)s, %(is_sold)s, + %(is_destroyed)s, %(notes)s, %(alterations_customisations)s, %(grading_company_name)s, + %(grading_score)s, %(subgrades)s, %(misprint_errors)s, %(miscut_errors)s, %(playability)s, + %(owner_user_id)s, %(ownership_status_name)s, %(trading_status_name)s, %(loaned_to_user_id)s, + %(loan_start_on)s, %(loan_end_on)s, %(provenance)s, %(signed_by_names)s, + %(signature_condition_name)s, %(active)s, %(created_by_user_id)s, + %(updated_last_by_user_id)s, %(change_set_id)s, + %(set_code)s, %(collector_number)s, %(display_order)s, %(name)s, + %(is_token)s, %(token_rear_side_name)s, %(token_rear_side_set_code)s, %(token_rear_side_collector_number)s + ) + """ + + cursor.execute(insert_query, data) + inserted_count += 1 + + if (index + 1) % 100 == 0: + print(f"Processed {index + 1} rows...") + try: + connection.commit() + except psycopg2.Error as e: + print(f'error: {e}') + connection.rollback() + + + except Exception as e: + print(f"Error processing row {index + 2}: {str(e)}") + skipped_count += 1 + connection.rollback() + continue + + # Final commit + try: + connection.commit() + except psycopg2.Error as e: + print(f'error: {e}') + connection.rollback() + + print(f"\nImport completed:") + print(f"- Records inserted: {inserted_count}") + print(f"- Records skipped: {skipped_count}") + + cursor.close() + +def main(): + """Main function to run the import""" + # file_path = '/home/teddy/Documents/Lifestyle/Trading Cards/Trading Cards upload.xlsx' + file_path = '/media/teddy/3_6GB-SSD-Storage/Documents/Lifestyle/Trading Cards/Trading Cards upload.xlsx' + minimum_row_number = 394 # ROW NUMBER - 1, or Inventory ID + + # Connect to database + try: + print("Connecting to database...") + connection = psycopg2.connect(**DB_CONFIG) + print("Connected successfully!") + + # Process the spreadsheet + print(f"\nProcessing {file_path}...") + process_spreadsheet(file_path, connection, minimum_row_number) + + except Exception as e: + print(f"Error: {str(e)}") + sys.exit(1) + finally: + if 'connection' in locals() and connection: + connection.close() + print("\nDatabase connection closed.") + +if __name__ == "__main__": + # Configuration notes: + # 1. Update DB_CONFIG with your actual database credentials + # 2. Set DEFAULT_USER_ID to your user_id in the system + # 3. Ensure TCG_MTG_Card table has records with matching Key IDs + # 4. Ensure TCG_MTG_Finish and TCG_Condition tables have the referenced IDs + + main() \ No newline at end of file diff --git a/python/mtg_booster_box_price_cardmarket_fetcher.py b/python/mtg_booster_box_price_cardmarket_fetcher.py new file mode 100644 index 0000000..d91045e --- /dev/null +++ b/python/mtg_booster_box_price_cardmarket_fetcher.py @@ -0,0 +1,234 @@ +import pandas as pd +from openpyxl import load_workbook +import requests +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.chrome.service import Service +import re +import time + +ITEM_SHIPPING_COST_IN = 8 + +def get_eur_to_gbp_rate(): + """Fetch current EUR to GBP conversion rate""" + try: + response = requests.get('https://api.exchangerate-api.com/v4/latest/EUR', timeout=10) + data = response.json() + return data['rates']['GBP'] + except Exception as e: + print(f"Error fetching exchange rate: {e}") + print("Using fallback rate: 0.85") + return 0.85 + +def parse_cardmarket_price(price_text): + """Convert '141,30 €' format to float in EUR""" + if not price_text: + return None + price_clean = re.sub(r'[^\d,]', '', price_text) + price_clean = price_clean.replace(',', '.') + try: + return float(price_clean) + except ValueError: + return None + +def setup_driver(): + """Setup Chrome driver with visible window""" + chrome_options = Options() + # Remove headless mode to see the browser + # chrome_options.add_argument('--headless') + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--disable-dev-shm-usage') + chrome_options.add_argument('--disable-blink-features=AutomationControlled') + chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36') + chrome_options.add_argument('--window-size=1920,1080') + + try: + driver = webdriver.Chrome(options=chrome_options) + return driver + except Exception as e: + print(f"Error setting up Chrome driver: {e}") + print("Make sure Chrome and chromedriver are installed") + return None + +def scrape_cardmarket_price_selenium(driver, url): + """Scrape price from Card Market URL using Selenium""" + try: + print(f" Loading page...") + driver.get(url) + + # Wait for page to load + time.sleep(3) + + print(f" Page title: {driver.title}") + + # Try multiple selector strategies + selectors = [ + # Original selector + '#table > div:nth-child(1) > div.table-body > .row.article-row.g-0:nth-child(1) > div.col-offer > div.price-container > div > div:nth-child(1) > span:nth-child(1)', + # Simpler selectors + #'.article-row .price-container span', + #'span.price', + #'div.price span', + #'.font-weight-bold.color-primary', + ] + + for i, selector in enumerate(selectors): + try: + elements = driver.find_elements(By.CSS_SELECTOR, selector) + print(f" Selector {i+1}: Found {len(elements)} elements") + for elem in elements[:3]: # Check first 3 + text = elem.text + print(f" Text: '{text}'") + if '€' in text and re.search(r'\d', text): + print(f" ✓ Found price with selector {i+1}: {text}") + # input("Confirm") + return text + except Exception as e: + print(f" Selector {i+1} failed: {e}") + + """ + # Try finding by text pattern + print(f" Trying to find price by text pattern...") + all_elements = driver.find_elements(By.XPATH, "//*[contains(text(), '€')]") + print(f" Found {len(all_elements)} elements containing '€'") + + for elem in all_elements[:10]: # Check first 10 + text = elem.text + if re.search(r'\d+[,\.]\d+\s*€', text): + print(f" ✓ Found price by pattern: {text}") + return text + """ + + print(f" ✗ No price found") + input("Press Enter to continue to next URL...") + return None + + except Exception as e: + print(f" Error: {e}") + return None + +def main(): + workbook_name = 'TCG Sole Trader Copy.xlsx' + sheet_name = 'Sourcing' + + print("Loading workbook...") + wb = load_workbook(workbook_name) + + if sheet_name not in wb.sheetnames: + print(f"Error: Sheet '{sheet_name}' not found") + return + + sheet = wb[sheet_name] + + # Find table boundaries + table_found = False + start_row = None + for row in range(1, sheet.max_row + 1): + if sheet.cell(row, 1).value == 'tbl_Sourcing' or 'Source Name' in str(sheet.cell(row, 3).value): + start_row = row + 1 + table_found = True + break + + if not table_found or not start_row: + for row in range(1, min(20, sheet.max_row + 1)): + if 'Source Name' in str(sheet.cell(row, 3).value): + start_row = row + 1 + table_found = True + break + + if not table_found: + print("Error: Could not find table 'tbl_Sourcing' or 'Source Name' column") + return + + # Find column indices + header_row = start_row - 1 + source_name_col = None + source_link_col = None + source_unit_cost_col = None + + for col in range(1, sheet.max_column + 1): + header = str(sheet.cell(header_row, col).value).strip() + if 'Source Name' in header: + source_name_col = col + elif 'Source Link' in header: + source_link_col = col + elif 'Source Unit Cost' in header: + source_unit_cost_col = col + + if not source_name_col: + source_name_col = 3 + + print(f"Starting from row {start_row}") + print(f"Columns - Source Name: {source_name_col}, Source Link: {source_link_col}, Source Unit Cost: {source_unit_cost_col}") + + if not all([source_link_col, source_unit_cost_col]): + print("Error: Could not find required columns") + return + + # Get EUR to GBP rate + eur_to_gbp = get_eur_to_gbp_rate() + print(f"Using EUR to GBP rate: {eur_to_gbp}") + + # Setup Selenium driver + print("Setting up browser automation (browser will be visible)...") + driver = setup_driver() + if not driver: + return + + try: + # Process rows + processed_count = 0 + updated_count = 0 + + for row in range(start_row, sheet.max_row + 1): + source_name = sheet.cell(row, source_name_col).value + source_link = sheet.cell(row, source_link_col).value + + # Check if row is empty + if not source_name and not source_link: + break + + # Check conditions + if source_name == "Card Market" and source_link and str(source_link).strip(): + processed_count += 1 + print(f"\n{'='*60}") + print(f"Processing row {row}: {source_link}") + print(f"{'='*60}") + + # Scrape price + price_text = scrape_cardmarket_price_selenium(driver, source_link) + + if price_text: + print(f" Found price: {price_text}") + + # Parse and convert + eur_price = parse_cardmarket_price(price_text) + if eur_price: + gbp_price = eur_price * eur_to_gbp + print(f" Converted: €{eur_price:.2f} → £{gbp_price:.2f}") + + # Update cell + sheet.cell(row, source_unit_cost_col).value = gbp_price + ITEM_SHIPPING_COST_IN + updated_count += 1 + else: + print(f" Error: Could not parse price") + else: + print(f" Error: Could not find price on page") + + # Save workbook + print(f"\n{'='*60}") + print(f"Saving workbook...") + wb.save(workbook_name) + + print(f"\nComplete!") + print(f"Processed: {processed_count} Card Market entries") + print(f"Updated: {updated_count} prices") + + finally: + driver.quit() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/mtg_booster_expected_value_fetcher.py b/python/mtg_booster_expected_value_fetcher.py new file mode 100644 index 0000000..da8fbbb --- /dev/null +++ b/python/mtg_booster_expected_value_fetcher.py @@ -0,0 +1,248 @@ +import pandas as pd +from openpyxl import load_workbook +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.chrome.options import Options +import re +import time + +def setup_driver(headless=True): + """Setup Chrome driver""" + chrome_options = Options() + if headless: + chrome_options.add_argument('--headless') + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--disable-dev-shm-usage') + chrome_options.add_argument('--disable-blink-features=AutomationControlled') + chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36') + chrome_options.add_argument('--window-size=1920,1080') + + try: + driver = webdriver.Chrome(options=chrome_options) + return driver + except Exception as e: + print(f"Error setting up Chrome driver: {e}") + print("Make sure Chrome and chromedriver are installed") + return None + +def parse_price_value(text): + """Extract numeric value from price string like '$5.50' or '€5,50'""" + if not text: + return None + # Remove currency symbols and extract numbers + cleaned = re.sub(r'[^\d,.\-]', '', text) + # Replace comma with period for decimal + cleaned = cleaned.replace(',', '.') + try: + return float(cleaned) + except ValueError: + return None + +def scrape_mtg_stocks_values(driver, url): + """Scrape expected value and market value from MTG Stocks""" + try: + print(f" Loading page...") + driver.get(url) + + # Wait for table to load + time.sleep(3) + + # Valid booster types to match + valid_booster_types = [ + 'Play Booster', + 'Set Booster', + 'Booster', + 'Play Booster Pack', + 'Set Booster Pack', + 'Booster Pack' + ] + + # Find all rows in the table + row_selector = 'mtg-sets-expected-value > mtg-product-tree > .table-responsive > table > tbody:nth-child(2) > tr' + rows = driver.find_elements(By.CSS_SELECTOR, row_selector) + + print(f" Found {len(rows)} rows in table") + + for row in rows: + try: + # Get the booster type from first column + booster_type_elem = row.find_element(By.CSS_SELECTOR, 'td:nth-child(1) > div.d-flex.align-items-center:nth-child(1) > a:nth-child(2)') + booster_type = booster_type_elem.text.strip() + + print(f" Checking row: '{booster_type}'") + + # Check if this matches our valid types + if booster_type in valid_booster_types: + print(f" ✓ Match found: '{booster_type}'") + + # Get expected value (3rd column) + expected_value_elem = row.find_element(By.CSS_SELECTOR, 'td:nth-child(3)') + expected_value_text = expected_value_elem.text.strip() + + # Get market value (5th column) + market_value_elem = row.find_element(By.CSS_SELECTOR, 'td:nth-child(5)') + market_value_text = market_value_elem.text.strip() + + print(f" Expected Value: '{expected_value_text}'") + print(f" Market Value: '{market_value_text}'") + + # Parse values + expected_value = parse_price_value(expected_value_text) + market_value = parse_price_value(market_value_text) + + return { + 'expected_value': expected_value, + 'market_value': market_value, + 'found': True + } + + except Exception as e: + # Row doesn't match structure, continue to next + continue + + print(f" ✗ No matching booster type found") + return { + 'expected_value': None, + 'market_value': None, + 'found': False + } + + except Exception as e: + print(f" Error: {e}") + return { + 'expected_value': None, + 'market_value': None, + 'found': False + } + +def main(): + workbook_name = 'TCG Sole Trader Copy.xlsx' + sheet_name = 'MTG Set' + + print("Loading workbook...") + wb = load_workbook(workbook_name) + + if sheet_name not in wb.sheetnames: + print(f"Error: Sheet '{sheet_name}' not found") + return + + sheet = wb[sheet_name] + + # Find table boundaries and columns + table_found = False + start_row = None + header_row = None + + # Search for table header + print("max sheet column: ", str(sheet.max_column)) + + for row in range(2, max(50, sheet.max_row + 1)): + cell_value = str(sheet.cell(row, 1).value) + # Check multiple columns for table indicators + for col in range(1, max(10, sheet.max_column + 1)): + cell_value = str(sheet.cell(row, col).value) + if 'EV MTG Stocks Link' in cell_value: + header_row = row + start_row = row + 1 + table_found = True + break + if table_found: + break + + if not table_found: + print("Error: Could not find 'EV MTG Stocks Link' column") + return + + print(f"Found table header at row {header_row}") + print(f"Starting from row {start_row}") + + # Find column indices + ev_link_col = None + expected_value_col = None + market_value_col = None + + for col in range(1, sheet.max_column + 1): + header = str(sheet.cell(header_row, col).value).strip() + if 'EV MTG Stocks Link' in header: + ev_link_col = col + elif 'Play Booster Expected Market Value' in header: + expected_value_col = col + elif 'Play Boost Sealed Market Value' in header: + market_value_col = col + + print(f"Columns - EV Link: {ev_link_col}, Expected Value: {expected_value_col}, Market Value: {market_value_col}") + + if not all([ev_link_col, expected_value_col, market_value_col]): + print("Error: Could not find all required columns") + print(f" EV MTG Stocks Link: {'Found' if ev_link_col else 'NOT FOUND'}") + print(f" Play Booster Expected Market Value: {'Found' if expected_value_col else 'NOT FOUND'}") + print(f" Play Boost Sealed Market Value: {'Found' if market_value_col else 'NOT FOUND'}") + return + + # Setup Selenium driver + print("Setting up browser automation...") + driver = setup_driver(headless=False) # Set to False to see browser + if not driver: + return + + try: + # Process rows + processed_count = 0 + updated_count = 0 + cleared_count = 0 + + for row in range(start_row, sheet.max_row + 1): + ev_link = sheet.cell(row, ev_link_col).value + + # Check if row is empty + if not ev_link: + # Check if we've passed the end of the table + empty_count = 0 + for check_col in range(1, min(10, sheet.max_column + 1)): + if not sheet.cell(row, check_col).value: + empty_count += 1 + if empty_count >= 5: # If most columns are empty, assume end of table + break + continue + + processed_count += 1 + print(f"\n{'='*80}") + print(f"Processing row {row}: {ev_link}") + print(f"{'='*80}") + + # Scrape values + result = scrape_mtg_stocks_values(driver, ev_link) + + if result['found']: + # Update cells with found values + sheet.cell(row, expected_value_col).value = result['expected_value'] + sheet.cell(row, market_value_col).value = result['market_value'] + updated_count += 1 + print(f" ✓ Updated - Expected: {result['expected_value']}, Market: {result['market_value']}") + else: + # Clear cells - no matching booster type found + sheet.cell(row, expected_value_col).value = '' + sheet.cell(row, market_value_col).value = '' + cleared_count += 1 + print(f" ✗ Cleared values - no matching booster type found") + + # Small delay between requests + time.sleep(2) + + # Save workbook + print(f"\n{'='*80}") + print(f"Saving workbook...") + wb.save(workbook_name) + + print(f"\nComplete!") + print(f"Processed: {processed_count} entries") + print(f"Updated: {updated_count} entries") + print(f"Cleared: {cleared_count} entries (no matching data)") + + finally: + driver.quit() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..459b946 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,11 @@ +# MTG Card Import +pandas +psycopg2 +numpy +openpyxl + + +# MTG Booster Box Price CardMarket +requests +beautifulsoup4 +selenium \ No newline at end of file diff --git a/sql/10500_tbl_TCG_Change_Set.sql b/sql/10500_tbl_TCG_Change_Set.sql new file mode 100644 index 0000000..b5bcf3c --- /dev/null +++ b/sql/10500_tbl_TCG_Change_Set.sql @@ -0,0 +1,7 @@ + +CREATE TABLE tcg.public.TCG_Change_Set ( + change_set_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , comment VARCHAR(500) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL +); \ No newline at end of file diff --git a/sql/10504_tbl_TCG_User.sql b/sql/10504_tbl_TCG_User.sql new file mode 100644 index 0000000..b9dd3a7 --- /dev/null +++ b/sql/10504_tbl_TCG_User.sql @@ -0,0 +1,25 @@ + +CREATE TABLE tcg.public.TCG_User ( + user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , user_auth0_id VARCHAR(200) + , firstname VARCHAR(250) + , surname VARCHAR(250) + , email VARCHAR(256) + , is_email_verified BOOLEAN NOT NULL DEFAULT FALSE + , is_super_user BOOLEAN NOT NULL DEFAULT FALSE + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_User_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_User_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_Change_Set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/10505_tbl_TCG_User_Audit.sql b/sql/10505_tbl_TCG_User_Audit.sql new file mode 100644 index 0000000..03801ff --- /dev/null +++ b/sql/10505_tbl_TCG_User_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_User_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , user_id INT NOT NULL + , CONSTRAINT FK_TCG_User_Audit_user_id + FOREIGN KEY (user_id) + REFERENCES tcg.public.TCG_User(user_id) + , name_field VARCHAR(255) NOT NULL + , value_prev VARCHAR(500) + , value_new VARCHAR(500) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_User_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); \ No newline at end of file diff --git a/sql/11001_tbl_TCG_Condition.sql b/sql/11001_tbl_TCG_Condition.sql new file mode 100644 index 0000000..91a31b1 --- /dev/null +++ b/sql/11001_tbl_TCG_Condition.sql @@ -0,0 +1,25 @@ +CREATE TABLE tcg.public.TCG_Condition ( + condition_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , name VARCHAR(250) NOT NULL + , code VARCHAR(250) NOT NULL + , description TEXT NOT NULL + , summary VARCHAR(250) NOT NULL + , us_name VARCHAR(250) NOT NULL + , price_ratio_min DECIMAL(7, 1) + , price_ratio_max DECIMAL(7, 1) + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_Condition_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_Condition_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_Condition_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11002_tbl_TCG_Condition_Audit.sql b/sql/11002_tbl_TCG_Condition_Audit.sql new file mode 100644 index 0000000..3a009a3 --- /dev/null +++ b/sql/11002_tbl_TCG_Condition_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_Condition_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , condition_id INT NOT NULL + , CONSTRAINT FK_TCG_Condition_Audit_condition_id + FOREIGN KEY (condition_id) + REFERENCES tcg.public.TCG_Condition(condition_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_Condition_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11101_tbl_TCG_MTG_Finish.sql b/sql/11101_tbl_TCG_MTG_Finish.sql new file mode 100644 index 0000000..4fe202e --- /dev/null +++ b/sql/11101_tbl_TCG_MTG_Finish.sql @@ -0,0 +1,22 @@ +CREATE TABLE tcg.public.TCG_MTG_Finish ( + finish_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , name VARCHAR(250) NOT NULL + , description TEXT NOT NULL + , price_ratio_min DECIMAL(6, 1) + , price_ratio_max DECIMAL(6, 1) + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Finish_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Finish_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Finish_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11102_tbl_TCG_MTG_Finish_Audit.sql b/sql/11102_tbl_TCG_MTG_Finish_Audit.sql new file mode 100644 index 0000000..a6f30ba --- /dev/null +++ b/sql/11102_tbl_TCG_MTG_Finish_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Finish_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , finish_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Finish_Audit_finish_id + FOREIGN KEY (finish_id) + REFERENCES tcg.public.TCG_MTG_Finish(finish_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Finish_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11130_tbl_TCG_MTG_Card.sql b/sql/11130_tbl_TCG_MTG_Card.sql new file mode 100644 index 0000000..9664f13 --- /dev/null +++ b/sql/11130_tbl_TCG_MTG_Card.sql @@ -0,0 +1,131 @@ +CREATE TABLE tcg.public.TCG_MTG_Card ( + card_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , all_parts TEXT + , arena_id INTEGER + , artist TEXT + , artist_ids VARCHAR(250) + , booster BOOLEAN + , border_color TEXT + , card_back_id VARCHAR(250) + , card_faces TEXT + , cardmarket_id INTEGER + , cmc DECIMAL(8, 1) + , collector_number TEXT + , color_identity TEXT + , colors TEXT + , content_warning BOOLEAN + , digital BOOLEAN + , edhrec_rank INTEGER + , finishes TEXT + , flavor_name TEXT + , flavor_text TEXT + , foil BOOLEAN + , frame TEXT + , full_art BOOLEAN + , game_changer BOOLEAN + , games TEXT + , hand_modifier TEXT + , highres_image BOOLEAN + , id VARCHAR(100) + , image_status TEXT + , image_uri_art_crop TEXT + , image_uri_border_crop TEXT + , image_uri_large TEXT + , image_uri_normal TEXT + , image_uri_png TEXT + , image_uri_small TEXT + , keywords TEXT + , lang TEXT + , layout TEXT + , legal_alchemy BOOLEAN + , legal_brawl BOOLEAN + , legal_commander BOOLEAN + , legal_duel BOOLEAN + , legal_future BOOLEAN + , legal_gladiator BOOLEAN + , legal_historic BOOLEAN + , legal_legacy BOOLEAN + , legal_modern BOOLEAN + , legal_oathbreaker BOOLEAN + , legal_oldschool BOOLEAN + , legal_pauper BOOLEAN + , legal_paupercommander BOOLEAN + , legal_penny BOOLEAN + , legal_pioneer BOOLEAN + , legal_predh BOOLEAN + , legal_premodern BOOLEAN + , legal_standard BOOLEAN + , legal_standardbrawl BOOLEAN + , legal_timeless BOOLEAN + , legal_vintage BOOLEAN + , life_modifier TEXT + , loyalty TEXT + , mana_cost TEXT + , mtgo_id INTEGER + , multiverse_ids VARCHAR(250) + , name TEXT NOT NULL + , nonfoil BOOLEAN + , oracle_id VARCHAR(100) + , oracle_text TEXT + , oversized BOOLEAN + , penny_rank INTEGER + , power TEXT + , preview_date DATE + , preview_source TEXT + , preview_source_uri TEXT + , price_eur DECIMAL(10, 3) + , price_eur_foil DECIMAL(10, 3) + , price_tix DECIMAL(10, 3) + , price_usd DECIMAL(10, 3) + , price_usd_etched DECIMAL(10, 3) + , price_usd_foil DECIMAL(10, 3) + , printed_name TEXT + , printed_text TEXT + , printed_type_line TEXT + , prints_search_uri TEXT + , produced_mana TEXT + , promo BOOLEAN + , purchase_cardhoarder TEXT + , purchase_cardmarket TEXT + , purchase_tcgplayer TEXT + , rarity TEXT + , released_at DATE + , reprint BOOLEAN + , reserved BOOLEAN + , rulings_uri TEXT + , scryfall_set_uri TEXT + , scryfall_uri TEXT + , security_stamp TEXT + , set TEXT + , set_id VARCHAR(100) + , set_name TEXT + , set_search_uri TEXT + , set_type TEXT + , set_uri TEXT + , story_spotlight BOOLEAN + , tcgplayer_id INTEGER + , textless BOOLEAN + , toughness TEXT + , type_line TEXT + , uri TEXT + , uri_edhrec TEXT + , uri_gatherer TEXT + , uri_tcgplayer_infinite_articles TEXT + , uri_tcgplayer_infinite_decks TEXT + , variation BOOLEAN + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Card_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Card_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Card_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); \ No newline at end of file diff --git a/sql/11131_tbl_TCG_MTG_Card_Audit.sql b/sql/11131_tbl_TCG_MTG_Card_Audit.sql new file mode 100644 index 0000000..3f60f9a --- /dev/null +++ b/sql/11131_tbl_TCG_MTG_Card_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Card_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , card_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Card_Audit_card_id + FOREIGN KEY (card_id) + REFERENCES tcg.public.TCG_MTG_Card(card_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Card_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11150_tbl_TCG_MTG_Inventory.sql b/sql/11150_tbl_TCG_MTG_Inventory.sql new file mode 100644 index 0000000..fcbb25c --- /dev/null +++ b/sql/11150_tbl_TCG_MTG_Inventory.sql @@ -0,0 +1,66 @@ +CREATE TABLE tcg.public.TCG_MTG_Inventory ( + inventory_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , card_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_card_id + FOREIGN KEY (card_id) + REFERENCES tcg.public.TCG_MTG_Card(card_id) + , finish_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_finish_id + FOREIGN KEY (finish_id) + REFERENCES tcg.public.TCG_MTG_Finish(finish_id) + , condition_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_condition_id + FOREIGN KEY (condition_id) + REFERENCES tcg.public.TCG_Condition(condition_id) + , sleeve_colour_name VARCHAR(250) + , location_name VARCHAR(250) + , acquired_from VARCHAR(500) + , acquired_on TIMESTAMP + , cost_gbp DECIMAL(6, 2) NOT NULL + , sale_price_gbp DECIMAL(6, 2) + , is_sold BOOLEAN NOT NULL DEFAULT FALSE + , is_destroyed BOOLEAN NOT NULL DEFAULT FALSE + , notes TEXT + , alterations_customisations VARCHAR(250) + , grading_company_name VARCHAR(250) + , grading_score VARCHAR(250) + , subgrades VARCHAR(250) + , misprint_errors VARCHAR(250) + , miscut_errors VARCHAR(250) + , playability VARCHAR(250) + , owner_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_owner_user_id + FOREIGN KEY (owner_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , ownership_status_name VARCHAR(250) + , trading_status_name VARCHAR(250) + , loaned_to_user_id INT + , CONSTRAINT FK_TCG_MTG_Inventory_loaned_to_user_id + FOREIGN KEY (loaned_to_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , loan_start_on TIMESTAMP + , loan_end_on TIMESTAMP + , provenance TEXT + , signed_by_names TEXT + , signature_condition_name VARCHAR(250) + , token_rear_side_card_id INT + , CONSTRAINT FK_TCG_MTG_Inventory_token_rear_side_card_id + FOREIGN KEY (token_rear_side_card_id) + REFERENCES tcg.public.TCG_MTG_Card(card_id) + , display_order INT NOT NULL + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11151_tbl_TCG_MTG_Inventory_Audit.sql b/sql/11151_tbl_TCG_MTG_Inventory_Audit.sql new file mode 100644 index 0000000..fef90f7 --- /dev/null +++ b/sql/11151_tbl_TCG_MTG_Inventory_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Inventory_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , inventory_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Audit_inventory_id + FOREIGN KEY (inventory_id) + REFERENCES tcg.public.TCG_MTG_Inventory(inventory_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11152_tbl_TCG_MTG_Inventory_Temp.sql b/sql/11152_tbl_TCG_MTG_Inventory_Temp.sql new file mode 100644 index 0000000..d670507 --- /dev/null +++ b/sql/11152_tbl_TCG_MTG_Inventory_Temp.sql @@ -0,0 +1,77 @@ + +CREATE TABLE tcg.public.TCG_MTG_Inventory_Temp ( + temp_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , inventory_id INT + , card_id INT + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_card_id + FOREIGN KEY (card_id) + REFERENCES tcg.public.TCG_MTG_Card(card_id) + , finish_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_finish_id + FOREIGN KEY (finish_id) + REFERENCES tcg.public.TCG_MTG_Finish(finish_id) + , condition_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_condition_id + FOREIGN KEY (condition_id) + REFERENCES tcg.public.TCG_Condition(condition_id) + , sleeve_colour_name VARCHAR(250) + , location_name VARCHAR(250) + , acquired_from VARCHAR(500) + , acquired_on TIMESTAMP + , cost_gbp DECIMAL(6, 2) NOT NULL + , sale_price_gbp DECIMAL(6, 2) + , is_sold BOOLEAN NOT NULL DEFAULT FALSE + , is_destroyed BOOLEAN NOT NULL DEFAULT FALSE + , notes TEXT + , alterations_customisations VARCHAR(250) + , grading_company_name VARCHAR(250) + , grading_score VARCHAR(250) + , subgrades VARCHAR(250) + , misprint_errors VARCHAR(250) + , miscut_errors VARCHAR(250) + , playability VARCHAR(250) + , owner_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_owner_user_id + FOREIGN KEY (owner_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , ownership_status_name VARCHAR(250) + , trading_status_name VARCHAR(250) + , loaned_to_user_id INT + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_loaned_to_user_id + FOREIGN KEY (loaned_to_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , loan_start_on TIMESTAMP + , loan_end_on TIMESTAMP + , provenance TEXT + , signed_by_names TEXT + , signature_condition_name VARCHAR(250) + , token_rear_side_card_id INT + , CONSTRAINT FK_TCG_MTG_Inventory_token_rear_side_card_id + FOREIGN KEY (token_rear_side_card_id) + REFERENCES tcg.public.TCG_MTG_Card(card_id) + , display_order INT NOT NULL + , active BOOLEAN NOT NULL DEFAULT TRUE + -- , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + -- , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Inventory_Temp_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) + + , set_code TEXT + , collector_number TEXT + , display_order INT + , name VARCHAR(250) + , is_token BOOLEAN + , token_rear_side_name VARCHAR(250) + , token_rear_side_set_code TEXT + , token_rear_side_collector_number TEXT +); diff --git a/sql/11196_tbl_TCG_MTG_Deck_Commander_Bracket.sql b/sql/11196_tbl_TCG_MTG_Deck_Commander_Bracket.sql new file mode 100644 index 0000000..526318e --- /dev/null +++ b/sql/11196_tbl_TCG_MTG_Deck_Commander_Bracket.sql @@ -0,0 +1,21 @@ +CREATE TABLE tcg.public.TCG_MTG_Deck_Commander_Bracket ( + commander_bracket_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , name VARCHAR(250) NOT NULL + , description TEXT + , display_order INT NOT NULL + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11197_tbl_TCG_MTG_Deck_Commander_Bracket_Audit.sql b/sql/11197_tbl_TCG_MTG_Deck_Commander_Bracket_Audit.sql new file mode 100644 index 0000000..49020fd --- /dev/null +++ b/sql/11197_tbl_TCG_MTG_Deck_Commander_Bracket_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Deck_Commander_Bracket_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , commander_bracket_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_Audit_commander_bracket_id + FOREIGN KEY (commander_bracket_id) + REFERENCES tcg.public.TCG_MTG_Deck_Commander_Bracket(commander_bracket_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11200_tbl_TCG_MTG_Deck.sql b/sql/11200_tbl_TCG_MTG_Deck.sql new file mode 100644 index 0000000..b725435 --- /dev/null +++ b/sql/11200_tbl_TCG_MTG_Deck.sql @@ -0,0 +1,24 @@ +CREATE TABLE tcg.public.TCG_MTG_Deck ( + deck_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , name VARCHAR(250) NOT NULL + , is_commander BOOLEAN NOT NULL DEFAULT TRUE + , commander_bracket_id INT + , CONSTRAINT FK_TCG_MTG_Deck_commander_bracket_id + FOREIGN KEY (commander_bracket_id) + REFERENCES tcg.public.TCG_MTG_Deck_Commander_Bracket(commander_bracket_id) + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11201_tbl_TCG_MTG_Deck_Audit.sql b/sql/11201_tbl_TCG_MTG_Deck_Audit.sql new file mode 100644 index 0000000..ea250a8 --- /dev/null +++ b/sql/11201_tbl_TCG_MTG_Deck_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Deck_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , deck_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Audit_deck_id + FOREIGN KEY (deck_id) + REFERENCES tcg.public.TCG_MTG_Deck(deck_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11204_tbl_TCG_MTG_Deck_Inventory_Link.sql b/sql/11204_tbl_TCG_MTG_Deck_Inventory_Link.sql new file mode 100644 index 0000000..806dad6 --- /dev/null +++ b/sql/11204_tbl_TCG_MTG_Deck_Inventory_Link.sql @@ -0,0 +1,27 @@ +CREATE TABLE tcg.public.TCG_MTG_Deck_Inventory_Link ( + link_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , deck_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_deck_id + FOREIGN KEY (deck_id) + REFERENCES tcg.public.TCG_MTG_Deck(deck_id) + , inventory_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_inventory_id + FOREIGN KEY (inventory_id) + REFERENCES tcg.public.TCG_MTG_Inventory(inventory_id) + , display_order INT NOT NULL + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP NOT NULL + , updated_last_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_updated_last_by_user_id + FOREIGN KEY (updated_last_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/11205_tbl_TCG_MTG_Deck_Inventory_Link_Audit.sql b/sql/11205_tbl_TCG_MTG_Deck_Inventory_Link_Audit.sql new file mode 100644 index 0000000..dde8d74 --- /dev/null +++ b/sql/11205_tbl_TCG_MTG_Deck_Inventory_Link_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Deck_Inventory_Link_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , link_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_Audit_link_id + FOREIGN KEY (link_id) + REFERENCES tcg.public.TCG_MTG_Deck_Inventory_Link(link_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/30500_tri_TCG_Change_Set.sql b/sql/30500_tri_TCG_Change_Set.sql new file mode 100644 index 0000000..bbdc082 --- /dev/null +++ b/sql/30500_tri_TCG_Change_Set.sql @@ -0,0 +1,20 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_Change_Set() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_Change_Set + BEFORE INSERT ON tcg.public.TCG_Change_Set + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_Change_Set() +; +CREATE TRIGGER TRI_before_update_TCG_Change_Set + BEFORE UPDATE ON tcg.public.TCG_Change_Set + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_Change_Set() +; \ No newline at end of file diff --git a/sql/30504_tri_TCG_User.sql b/sql/30504_tri_TCG_User.sql new file mode 100644 index 0000000..08dd792 --- /dev/null +++ b/sql/30504_tri_TCG_User.sql @@ -0,0 +1,76 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_User() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_User() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_User_Audit ( + user_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed user_auth0_id + SELECT NEW.user_id, 'user_auth0_id', OLD.user_auth0_id, NEW.user_auth0_id, NEW.change_set_id + WHERE OLD.user_auth0_id IS NOT DISTINCT FROM NEW.user_auth0_id + UNION + -- Changed firstname + SELECT NEW.user_id, 'firstname', OLD.firstname, NEW.firstname, NEW.change_set_id + WHERE OLD.firstname IS NOT DISTINCT FROM NEW.firstname + UNION + -- Changed surname + SELECT NEW.user_id, 'surname', OLD.surname, NEW.surname, NEW.change_set_id + WHERE OLD.surname IS NOT DISTINCT FROM NEW.surname + UNION + -- Changed email + SELECT NEW.user_id, 'email', OLD.email, NEW.email, NEW.change_set_id + WHERE OLD.email IS NOT DISTINCT FROM NEW.email + UNION + -- Changed is_email_verified + SELECT NEW.user_id, 'is_email_verified', OLD.is_email_verified::TEXT, NEW.is_email_verified::TEXT, NEW.change_set_id + WHERE OLD.is_email_verified IS NOT DISTINCT FROM NEW.is_email_verified + UNION + -- Changed is_super_user + SELECT NEW.user_id, 'is_super_user', OLD.is_super_user::TEXT, NEW.is_super_user::TEXT, NEW.change_set_id + WHERE OLD.is_super_user IS NOT DISTINCT FROM NEW.is_super_user + UNION + -- Changed active + SELECT NEW.user_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_User + BEFORE INSERT ON tcg.public.TCG_User + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_User() +; +CREATE TRIGGER TRI_before_update_TCG_User + BEFORE UPDATE ON tcg.public.TCG_User + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_User() +; \ No newline at end of file diff --git a/sql/31001_tri_TCG_Condition.sql b/sql/31001_tri_TCG_Condition.sql new file mode 100644 index 0000000..f02cf3e --- /dev/null +++ b/sql/31001_tri_TCG_Condition.sql @@ -0,0 +1,80 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_Condition() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_Condition() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_Condition_Audit ( + condition_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed name + SELECT NEW.condition_id, 'name', OLD.name, NEW.name, NEW.change_set_id + WHERE OLD.name IS NOT DISTINCT FROM NEW.name + UNION + -- Changed code + SELECT NEW.condition_id, 'code', OLD.code, NEW.code, NEW.change_set_id + WHERE OLD.code IS NOT DISTINCT FROM NEW.code + UNION + -- Changed description + SELECT NEW.condition_id, 'description', OLD.description, NEW.description, NEW.change_set_id + WHERE OLD.description IS NOT DISTINCT FROM NEW.description + UNION + -- Changed summary + SELECT NEW.condition_id, 'summary', OLD.summary, NEW.summary, NEW.change_set_id + WHERE OLD.summary IS NOT DISTINCT FROM NEW.summary + UNION + -- Changed us_name + SELECT NEW.condition_id, 'us_name', OLD.us_name, NEW.us_name, NEW.change_set_id + WHERE OLD.us_name IS NOT DISTINCT FROM NEW.us_name + UNION + -- Changed price_ratio_min + SELECT NEW.condition_id, 'price_ratio_min', CAST(OLD.price_ratio_min AS VARCHAR), CAST(NEW.price_ratio_min AS VARCHAR), NEW.change_set_id + WHERE OLD.price_ratio_min IS NOT DISTINCT FROM NEW.price_ratio_min + UNION + -- Changed price_ratio_max + SELECT NEW.condition_id, 'price_ratio_max', CAST(OLD.price_ratio_max AS VARCHAR), CAST(NEW.price_ratio_max AS VARCHAR), NEW.change_set_id + WHERE OLD.price_ratio_max IS NOT DISTINCT FROM NEW.price_ratio_max + UNION + -- Changed active + SELECT NEW.condition_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_Condition + BEFORE INSERT ON tcg.public.TCG_Condition + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_Condition() +; +CREATE TRIGGER TRI_before_update_TCG_Condition + BEFORE UPDATE ON tcg.public.TCG_Condition + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_Condition() +; \ No newline at end of file diff --git a/sql/31101_tri_TCG_MTG_Finish.sql b/sql/31101_tri_TCG_MTG_Finish.sql new file mode 100644 index 0000000..13b3d1b --- /dev/null +++ b/sql/31101_tri_TCG_MTG_Finish.sql @@ -0,0 +1,68 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Finish() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Finish() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Finish_Audit ( + finish_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed name + SELECT NEW.finish_id, 'name', OLD.name, NEW.name, NEW.change_set_id + WHERE OLD.name IS NOT DISTINCT FROM NEW.name + UNION + -- Changed description + SELECT NEW.finish_id, 'description', OLD.description, NEW.description, NEW.change_set_id + WHERE OLD.description IS NOT DISTINCT FROM NEW.description + UNION + -- Changed price_ratio_min + SELECT NEW.finish_id, 'price_ratio_min', CAST(OLD.price_ratio_min AS VARCHAR), CAST(NEW.price_ratio_min AS VARCHAR), NEW.change_set_id + WHERE OLD.price_ratio_min IS NOT DISTINCT FROM NEW.price_ratio_min + UNION + -- Changed price_ratio_max + SELECT NEW.finish_id, 'price_ratio_max', CAST(OLD.price_ratio_max AS VARCHAR), CAST(NEW.price_ratio_max AS VARCHAR), NEW.change_set_id + WHERE OLD.price_ratio_max IS NOT DISTINCT FROM NEW.price_ratio_max + UNION + -- Changed active + SELECT NEW.finish_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Finish + BEFORE INSERT ON tcg.public.TCG_MTG_Finish + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Finish() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Finish + BEFORE UPDATE ON tcg.public.TCG_MTG_Finish + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Finish() +; \ No newline at end of file diff --git a/sql/31130_tri_TCG_MTG_Card.sql b/sql/31130_tri_TCG_MTG_Card.sql new file mode 100644 index 0000000..b66a2ba --- /dev/null +++ b/sql/31130_tri_TCG_MTG_Card.sql @@ -0,0 +1,496 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Card() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Card() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Card_Audit ( + card_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed all_parts + SELECT NEW.card_id, 'all_parts', OLD.all_parts, NEW.all_parts, NEW.change_set_id + WHERE OLD.all_parts IS NOT DISTINCT FROM NEW.all_parts + UNION + -- Changed arena_id + SELECT NEW.card_id, 'arena_id', CAST(OLD.arena_id AS VARCHAR), CAST(NEW.arena_id AS VARCHAR), NEW.change_set_id + WHERE OLD.arena_id IS NOT DISTINCT FROM NEW.arena_id + UNION + -- Changed artist + SELECT NEW.card_id, 'artist', OLD.artist, NEW.artist, NEW.change_set_id + WHERE OLD.artist IS NOT DISTINCT FROM NEW.artist + UNION + -- Changed artist_ids + SELECT NEW.card_id, 'artist_ids', OLD.artist_ids, NEW.artist_ids, NEW.change_set_id + WHERE OLD.artist_ids IS NOT DISTINCT FROM NEW.artist_ids + UNION + -- Changed booster + SELECT NEW.card_id, 'booster', OLD.booster::TEXT, NEW.booster::TEXT, NEW.change_set_id + WHERE OLD.booster IS NOT DISTINCT FROM NEW.booster + UNION + -- Changed border_color + SELECT NEW.card_id, 'border_color', OLD.border_color, NEW.border_color, NEW.change_set_id + WHERE OLD.border_color IS NOT DISTINCT FROM NEW.border_color + UNION + -- Changed card_back_id + SELECT NEW.card_id, 'card_back_id', OLD.card_back_id, NEW.card_back_id, NEW.change_set_id + WHERE OLD.card_back_id IS NOT DISTINCT FROM NEW.card_back_id + UNION + -- Changed cmc + SELECT NEW.card_id, 'cmc', CAST(OLD.cmc AS VARCHAR), CAST(NEW.cmc AS VARCHAR), NEW.change_set_id + WHERE OLD.cmc IS NOT DISTINCT FROM NEW.cmc + UNION + -- Changed collector_number + SELECT NEW.card_id, 'collector_number', OLD.collector_number, NEW.collector_number, NEW.change_set_id + WHERE OLD.collector_number IS NOT DISTINCT FROM NEW.collector_number + UNION + -- Changed color_identity + SELECT NEW.card_id, 'color_identity', OLD.color_identity, NEW.color_identity, NEW.change_set_id + WHERE OLD.color_identity IS NOT DISTINCT FROM NEW.color_identity + UNION + -- Changed colors + SELECT NEW.card_id, 'colors', OLD.colors, NEW.colors, NEW.change_set_id + WHERE OLD.colors IS NOT DISTINCT FROM NEW.colors + UNION + -- Changed content_warning + SELECT NEW.card_id, 'content_warning', OLD.content_warning::TEXT, NEW.content_warning::TEXT, NEW.change_set_id + WHERE OLD.content_warning IS NOT DISTINCT FROM NEW.content_warning + UNION + -- Changed digital + SELECT NEW.card_id, 'digital', OLD.digital::TEXT, NEW.digital::TEXT, NEW.change_set_id + WHERE OLD.digital IS NOT DISTINCT FROM NEW.digital + UNION + -- Changed edhrec_rank + SELECT NEW.card_id, 'edhrec_rank', CAST(OLD.edhrec_rank AS VARCHAR), CAST(NEW.edhrec_rank AS VARCHAR), NEW.change_set_id + WHERE OLD.edhrec_rank IS NOT DISTINCT FROM NEW.edhrec_rank + UNION + -- Changed finishes + SELECT NEW.card_id, 'finishes', OLD.finishes, NEW.finishes, NEW.change_set_id + WHERE OLD.finishes IS NOT DISTINCT FROM NEW.finishes + UNION + -- Changed flavor_name + SELECT NEW.card_id, 'flavor_name', OLD.flavor_name, NEW.flavor_name, NEW.change_set_id + WHERE OLD.flavor_name IS NOT DISTINCT FROM NEW.flavor_name + UNION + -- Changed flavor_text + SELECT NEW.card_id, 'flavor_text', OLD.flavor_text, NEW.flavor_text, NEW.change_set_id + WHERE OLD.flavor_text IS NOT DISTINCT FROM NEW.flavor_text + UNION + -- Changed foil + SELECT NEW.card_id, 'foil', OLD.foil::TEXT, NEW.foil::TEXT, NEW.change_set_id + WHERE OLD.foil IS NOT DISTINCT FROM NEW.foil + UNION + -- Changed frame + SELECT NEW.card_id, 'frame', OLD.frame, NEW.frame, NEW.change_set_id + WHERE OLD.frame IS NOT DISTINCT FROM NEW.frame + UNION + -- Changed full_art + SELECT NEW.card_id, 'full_art', OLD.full_art::TEXT, NEW.full_art::TEXT, NEW.change_set_id + WHERE OLD.full_art IS NOT DISTINCT FROM NEW.full_art + UNION + -- Changed game_changer + SELECT NEW.card_id, 'game_changer', OLD.game_changer::TEXT, NEW.game_changer::TEXT, NEW.change_set_id + WHERE OLD.game_changer IS NOT DISTINCT FROM NEW.game_changer + UNION + -- Changed games + SELECT NEW.card_id, 'games', OLD.games, NEW.games, NEW.change_set_id + WHERE OLD.games IS NOT DISTINCT FROM NEW.games + UNION + -- Changed hand_modifier + SELECT NEW.card_id, 'hand_modifier', OLD.hand_modifier, NEW.hand_modifier, NEW.change_set_id + WHERE OLD.hand_modifier IS NOT DISTINCT FROM NEW.hand_modifier + UNION + -- Changed highres_image + SELECT NEW.card_id, 'highres_image', OLD.highres_image::TEXT, NEW.highres_image::TEXT, NEW.change_set_id + WHERE OLD.highres_image IS NOT DISTINCT FROM NEW.highres_image + UNION + -- Changed id + SELECT NEW.card_id, 'id', CAST(OLD.id AS VARCHAR), CAST(NEW.id AS VARCHAR), NEW.change_set_id + WHERE OLD.id IS NOT DISTINCT FROM NEW.id + UNION + -- Changed image_status + SELECT NEW.card_id, 'image_status', OLD.image_status, NEW.image_status, NEW.change_set_id + WHERE OLD.image_status IS NOT DISTINCT FROM NEW.image_status + UNION + -- Changed image_uri_art_crop + SELECT NEW.card_id, 'image_uri_art_crop', OLD.image_uri_art_crop, NEW.image_uri_art_crop, NEW.change_set_id + WHERE OLD.image_uri_art_crop IS NOT DISTINCT FROM NEW.image_uri_art_crop + UNION + -- Changed image_uri_border_crop + SELECT NEW.card_id, 'image_uri_border_crop', OLD.image_uri_border_crop, NEW.image_uri_border_crop, NEW.change_set_id + WHERE OLD.image_uri_border_crop IS NOT DISTINCT FROM NEW.image_uri_border_crop + UNION + -- Changed image_uri_large + SELECT NEW.card_id, 'image_uri_large', OLD.image_uri_large, NEW.image_uri_large, NEW.change_set_id + WHERE OLD.image_uri_large IS NOT DISTINCT FROM NEW.image_uri_large + UNION + -- Changed image_uri_normal + SELECT NEW.card_id, 'image_uri_normal', OLD.image_uri_normal, NEW.image_uri_normal, NEW.change_set_id + WHERE OLD.image_uri_normal IS NOT DISTINCT FROM NEW.image_uri_normal + UNION + -- Changed image_uri_png + SELECT NEW.card_id, 'image_uri_png', OLD.image_uri_png, NEW.image_uri_png, NEW.change_set_id + WHERE OLD.image_uri_png IS NOT DISTINCT FROM NEW.image_uri_png + UNION + -- Changed image_uri_small + SELECT NEW.card_id, 'image_uri_small', OLD.image_uri_small, NEW.image_uri_small, NEW.change_set_id + WHERE OLD.image_uri_small IS NOT DISTINCT FROM NEW.image_uri_small + UNION + -- Changed keywords + SELECT NEW.card_id, 'keywords', OLD.keywords, NEW.keywords, NEW.change_set_id + WHERE OLD.keywords IS NOT DISTINCT FROM NEW.keywords + UNION + -- Changed lang + SELECT NEW.card_id, 'lang', OLD.lang, NEW.lang, NEW.change_set_id + WHERE OLD.lang IS NOT DISTINCT FROM NEW.lang + UNION + -- Changed layout + SELECT NEW.card_id, 'layout', OLD.layout, NEW.layout, NEW.change_set_id + WHERE OLD.layout IS NOT DISTINCT FROM NEW.layout + UNION + -- Changed legal_alchemy + SELECT NEW.card_id, 'legal_alchemy', OLD.legal_alchemy::TEXT, NEW.legal_alchemy::TEXT, NEW.change_set_id + WHERE OLD.legal_alchemy IS NOT DISTINCT FROM NEW.legal_alchemy + UNION + -- Changed legal_brawl + SELECT NEW.card_id, 'legal_brawl', OLD.legal_brawl::TEXT, NEW.legal_brawl::TEXT, NEW.change_set_id + WHERE OLD.legal_brawl IS NOT DISTINCT FROM NEW.legal_brawl + UNION + -- Changed legal_commander + SELECT NEW.card_id, 'legal_commander', OLD.legal_commander::TEXT, NEW.legal_commander::TEXT, NEW.change_set_id + WHERE OLD.legal_commander IS NOT DISTINCT FROM NEW.legal_commander + UNION + -- Changed legal_duel + SELECT NEW.card_id, 'legal_duel', OLD.legal_duel::TEXT, NEW.legal_duel::TEXT, NEW.change_set_id + WHERE OLD.legal_duel IS NOT DISTINCT FROM NEW.legal_duel + UNION + -- Changed legal_future + SELECT NEW.card_id, 'legal_future', OLD.legal_future::TEXT, NEW.legal_future::TEXT, NEW.change_set_id + WHERE OLD.legal_future IS NOT DISTINCT FROM NEW.legal_future + UNION + -- Changed legal_gladiator + SELECT NEW.card_id, 'legal_gladiator', OLD.legal_gladiator::TEXT, NEW.legal_gladiator::TEXT, NEW.change_set_id + WHERE OLD.legal_gladiator IS NOT DISTINCT FROM NEW.legal_gladiator + UNION + -- Changed legal_historic + SELECT NEW.card_id, 'legal_historic', OLD.legal_historic::TEXT, NEW.legal_historic::TEXT, NEW.change_set_id + WHERE OLD.legal_historic IS NOT DISTINCT FROM NEW.legal_historic + UNION + -- Changed legal_legacy + SELECT NEW.card_id, 'legal_legacy', OLD.legal_legacy::TEXT, NEW.legal_legacy::TEXT, NEW.change_set_id + WHERE OLD.legal_legacy IS NOT DISTINCT FROM NEW.legal_legacy + UNION + -- Changed legal_modern + SELECT NEW.card_id, 'legal_modern', OLD.legal_modern::TEXT, NEW.legal_modern::TEXT, NEW.change_set_id + WHERE OLD.legal_modern IS NOT DISTINCT FROM NEW.legal_modern + UNION + -- Changed legal_oathbreaker + SELECT NEW.card_id, 'legal_oathbreaker', OLD.legal_oathbreaker::TEXT, NEW.legal_oathbreaker::TEXT, NEW.change_set_id + WHERE OLD.legal_oathbreaker IS NOT DISTINCT FROM NEW.legal_oathbreaker + UNION + -- Changed legal_oldschool + SELECT NEW.card_id, 'legal_oldschool', OLD.legal_oldschool::TEXT, NEW.legal_oldschool::TEXT, NEW.change_set_id + WHERE OLD.legal_oldschool IS NOT DISTINCT FROM NEW.legal_oldschool + UNION + -- Changed legal_pauper + SELECT NEW.card_id, 'legal_pauper', OLD.legal_pauper::TEXT, NEW.legal_pauper::TEXT, NEW.change_set_id + WHERE OLD.legal_pauper IS NOT DISTINCT FROM NEW.legal_pauper + UNION + -- Changed legal_paupercommander + SELECT NEW.card_id, 'legal_paupercommander', OLD.legal_paupercommander::TEXT, NEW.legal_paupercommander::TEXT, NEW.change_set_id + WHERE OLD.legal_paupercommander IS NOT DISTINCT FROM NEW.legal_paupercommander + UNION + -- Changed legal_penny + SELECT NEW.card_id, 'legal_penny', OLD.legal_penny::TEXT, NEW.legal_penny::TEXT, NEW.change_set_id + WHERE OLD.legal_penny IS NOT DISTINCT FROM NEW.legal_penny + UNION + -- Changed legal_pioneer + SELECT NEW.card_id, 'legal_pioneer', OLD.legal_pioneer::TEXT, NEW.legal_pioneer::TEXT, NEW.change_set_id + WHERE OLD.legal_pioneer IS NOT DISTINCT FROM NEW.legal_pioneer + UNION + -- Changed legal_predh + SELECT NEW.card_id, 'legal_predh', OLD.legal_predh::TEXT, NEW.legal_predh::TEXT, NEW.change_set_id + WHERE OLD.legal_predh IS NOT DISTINCT FROM NEW.legal_predh + UNION + -- Changed legal_premodern + SELECT NEW.card_id, 'legal_premodern', OLD.legal_premodern::TEXT, NEW.legal_premodern::TEXT, NEW.change_set_id + WHERE OLD.legal_premodern IS NOT DISTINCT FROM NEW.legal_premodern + UNION + -- Changed legal_standard + SELECT NEW.card_id, 'legal_standard', OLD.legal_standard::TEXT, NEW.legal_standard::TEXT, NEW.change_set_id + WHERE OLD.legal_standard IS NOT DISTINCT FROM NEW.legal_standard + UNION + -- Changed legal_standardbrawl + SELECT NEW.card_id, 'legal_standardbrawl', OLD.legal_standardbrawl::TEXT, NEW.legal_standardbrawl::TEXT, NEW.change_set_id + WHERE OLD.legal_standardbrawl IS NOT DISTINCT FROM NEW.legal_standardbrawl + UNION + -- Changed legal_timeless + SELECT NEW.card_id, 'legal_timeless', OLD.legal_timeless::TEXT, NEW.legal_timeless::TEXT, NEW.change_set_id + WHERE OLD.legal_timeless IS NOT DISTINCT FROM NEW.legal_timeless + UNION + -- Changed legal_vintage + SELECT NEW.card_id, 'legal_vintage', OLD.legal_vintage::TEXT, NEW.legal_vintage::TEXT, NEW.change_set_id + WHERE OLD.legal_vintage IS NOT DISTINCT FROM NEW.legal_vintage + UNION + -- Changed life_modifier + SELECT NEW.card_id, 'life_modifier', OLD.life_modifier, NEW.life_modifier, NEW.change_set_id + WHERE OLD.life_modifier IS NOT DISTINCT FROM NEW.life_modifier + UNION + -- Changed loyalty + SELECT NEW.card_id, 'loyalty', OLD.loyalty, NEW.loyalty, NEW.change_set_id + WHERE OLD.loyalty IS NOT DISTINCT FROM NEW.loyalty + UNION + -- Changed mana_cost + SELECT NEW.card_id, 'mana_cost', OLD.mana_cost, NEW.mana_cost, NEW.change_set_id + WHERE OLD.mana_cost IS NOT DISTINCT FROM NEW.mana_cost + UNION + -- Changed mtgo_id + SELECT NEW.card_id, 'mtgo_id', CAST(OLD.mtgo_id AS VARCHAR), CAST(NEW.mtgo_id AS VARCHAR), NEW.change_set_id + WHERE OLD.mtgo_id IS NOT DISTINCT FROM NEW.mtgo_id + UNION + -- Changed multiverse_ids + SELECT NEW.card_id, 'multiverse_ids', OLD.multiverse_ids, NEW.multiverse_ids, NEW.change_set_id + WHERE OLD.multiverse_ids IS NOT DISTINCT FROM NEW.multiverse_ids + UNION + -- Changed name + SELECT NEW.card_id, 'name', OLD.name, NEW.name, NEW.change_set_id + WHERE OLD.name IS NOT DISTINCT FROM NEW.name + UNION + -- Changed nonfoil + SELECT NEW.card_id, 'nonfoil', OLD.nonfoil::TEXT, NEW.nonfoil::TEXT, NEW.change_set_id + WHERE OLD.nonfoil IS NOT DISTINCT FROM NEW.nonfoil + UNION + -- Changed oracle_id + SELECT NEW.card_id, 'oracle_id', CAST(OLD.oracle_id AS VARCHAR), CAST(NEW.oracle_id AS VARCHAR), NEW.change_set_id + WHERE OLD.oracle_id IS NOT DISTINCT FROM NEW.oracle_id + UNION + -- Changed oracle_text + SELECT NEW.card_id, 'oracle_text', OLD.oracle_text, NEW.oracle_text, NEW.change_set_id + WHERE OLD.oracle_text IS NOT DISTINCT FROM NEW.oracle_text + UNION + -- Changed oversized + SELECT NEW.card_id, 'oversized', OLD.oversized::TEXT, NEW.oversized::TEXT, NEW.change_set_id + WHERE OLD.oversized IS NOT DISTINCT FROM NEW.oversized + UNION + -- Changed penny_rank + SELECT NEW.card_id, 'penny_rank', CAST(OLD.penny_rank AS VARCHAR), CAST(NEW.penny_rank AS VARCHAR), NEW.change_set_id + WHERE OLD.penny_rank IS NOT DISTINCT FROM NEW.penny_rank + UNION + -- Changed power + SELECT NEW.card_id, 'power', OLD.power, NEW.power, NEW.change_set_id + WHERE OLD.power IS NOT DISTINCT FROM NEW.power + UNION + -- Changed preview_date + SELECT NEW.card_id, 'preview_date', TO_CHAR(OLD.preview_date, 'YYYY-MM-DD'), TO_CHAR(NEW.preview_date, 'YYYY-MM-DD'), NEW.change_set_id + WHERE OLD.preview_date IS NOT DISTINCT FROM NEW.preview_date + UNION + -- Changed preview_source + SELECT NEW.card_id, 'preview_source', OLD.preview_source, NEW.preview_source, NEW.change_set_id + WHERE OLD.preview_source IS NOT DISTINCT FROM NEW.preview_source + UNION + -- Changed preview_source_uri + SELECT NEW.card_id, 'preview_source_uri', OLD.preview_source_uri, NEW.preview_source_uri, NEW.change_set_id + WHERE OLD.preview_source_uri IS NOT DISTINCT FROM NEW.preview_source_uri + UNION + -- Changed price_eur + SELECT NEW.card_id, 'price_eur', CAST(OLD.price_eur AS VARCHAR), CAST(NEW.price_eur AS VARCHAR), NEW.change_set_id + WHERE OLD.price_eur IS NOT DISTINCT FROM NEW.price_eur + UNION + -- Changed price_eur_foil + SELECT NEW.card_id, 'price_eur_foil', CAST(OLD.price_eur_foil AS VARCHAR), CAST(NEW.price_eur_foil AS VARCHAR), NEW.change_set_id + WHERE OLD.price_eur_foil IS NOT DISTINCT FROM NEW.price_eur_foil + UNION + -- Changed price_tix + SELECT NEW.card_id, 'price_tix', CAST(OLD.price_tix AS VARCHAR), CAST(NEW.price_tix AS VARCHAR), NEW.change_set_id + WHERE OLD.price_tix IS NOT DISTINCT FROM NEW.price_tix + UNION + -- Changed price_usd + SELECT NEW.card_id, 'price_usd', CAST(OLD.price_usd AS VARCHAR), CAST(NEW.price_usd AS VARCHAR), NEW.change_set_id + WHERE OLD.price_usd IS NOT DISTINCT FROM NEW.price_usd + UNION + -- Changed price_usd_etched + SELECT NEW.card_id, 'price_usd_etched', CAST(OLD.price_usd_etched AS VARCHAR), CAST(NEW.price_usd_etched AS VARCHAR), NEW.change_set_id + WHERE OLD.price_usd_etched IS NOT DISTINCT FROM NEW.price_usd_etched + UNION + -- Changed price_usd_foil + SELECT NEW.card_id, 'price_usd_foil', CAST(OLD.price_usd_foil AS VARCHAR), CAST(NEW.price_usd_foil AS VARCHAR), NEW.change_set_id + WHERE OLD.price_usd_foil IS NOT DISTINCT FROM NEW.price_usd_foil + UNION + -- Changed printed_name + SELECT NEW.card_id, 'printed_name', OLD.printed_name, NEW.printed_name, NEW.change_set_id + WHERE OLD.printed_name IS NOT DISTINCT FROM NEW.printed_name + UNION + -- Changed printed_text + SELECT NEW.card_id, 'printed_text', OLD.printed_text, NEW.printed_text, NEW.change_set_id + WHERE OLD.printed_text IS NOT DISTINCT FROM NEW.printed_text + UNION + -- Changed printed_type_line + SELECT NEW.card_id, 'printed_type_line', OLD.printed_type_line, NEW.printed_type_line, NEW.change_set_id + WHERE OLD.printed_type_line IS NOT DISTINCT FROM NEW.printed_type_line + UNION + -- Changed prints_search_uri + SELECT NEW.card_id, 'printed_search_uri', OLD.printed_search_uri, NEW.printed_search_uri, NEW.change_set_id + WHERE OLD.printed_search_uri IS NOT DISTINCT FROM NEW.printed_search_uri + UNION + -- Changed produced_mana + SELECT NEW.card_id, 'produced_mana', OLD.produced_mana, NEW.produced_mana, NEW.change_set_id + WHERE OLD.produced_mana IS NOT DISTINCT FROM NEW.produced_mana + UNION + -- Changed promo + SELECT NEW.card_id, 'promo', OLD.promo::TEXT, NEW.promo::TEXT, NEW.change_set_id + WHERE OLD.promo IS NOT DISTINCT FROM NEW.promo + UNION + -- Changed purchase_cardhoarder + SELECT NEW.card_id, 'purchase_cardhoarder', OLD.purchase_cardhoarder, NEW.purchase_cardhoarder, NEW.change_set_id + WHERE OLD.purchase_cardhoarder IS NOT DISTINCT FROM NEW.purchase_cardhoarder + UNION + -- Changed purchase_cardmarket + SELECT NEW.card_id, 'purchase_cardmarket', OLD.purchase_cardmarket, NEW.purchase_cardmarket, NEW.change_set_id + WHERE OLD.purchase_cardmarket IS NOT DISTINCT FROM NEW.purchase_cardmarket + UNION + -- Changed purchase_tcgplayer + SELECT NEW.card_id, 'purchase_tcgplayer', OLD.purchase_tcgplayer, NEW.purchase_tcgplayer, NEW.change_set_id + WHERE OLD.purchase_tcgplayer IS NOT DISTINCT FROM NEW.purchase_tcgplayer + UNION + -- Changed rarity + SELECT NEW.card_id, 'rarity', OLD.rarity, NEW.rarity, NEW.change_set_id + WHERE OLD.rarity IS NOT DISTINCT FROM NEW.rarity + UNION + -- Changed released_at + SELECT NEW.card_id, 'released_at', TO_CHAR(OLD.released_at, 'YYYY-MM-DD'), TO_CHAR(NEW.released_at, 'YYYY-MM-DD'), NEW.change_set_id + WHERE OLD.released_at IS NOT DISTINCT FROM NEW.released_at + UNION + -- Changed reprint + SELECT NEW.card_id, 'reprint', OLD.reprint::TEXT, NEW.reprint::TEXT, NEW.change_set_id + WHERE OLD.reprint IS NOT DISTINCT FROM NEW.reprint + UNION + -- Changed reserved + SELECT NEW.card_id, 'reserved', OLD.reserved::TEXT, NEW.reserved::TEXT, NEW.change_set_id + WHERE OLD.reserved IS NOT DISTINCT FROM NEW.reserved + UNION + -- Changed rulings_uri + SELECT NEW.card_id, 'rulings_uri', OLD.rulings_uri, NEW.rulings_uri, NEW.change_set_id + WHERE OLD.rulings_uri IS NOT DISTINCT FROM NEW.rulings_uri + UNION + -- Changed scryfall_set_uri + SELECT NEW.card_id, 'scryfall_set_uri', OLD.scryfall_set_uri, NEW.scryfall_set_uri, NEW.change_set_id + WHERE OLD.scryfall_set_uri IS NOT DISTINCT FROM NEW.scryfall_set_uri + UNION + -- Changed scryfall_uri + SELECT NEW.card_id, 'scryfall_uri', OLD.scryfall_uri, NEW.scryfall_uri, NEW.change_set_id + WHERE OLD.scryfall_uri IS NOT DISTINCT FROM NEW.scryfall_uri + UNION + -- Changed security_stamp + SELECT NEW.card_id, 'security_stamp', OLD.security_stamp, NEW.security_stamp, NEW.change_set_id + WHERE OLD.security_stamp IS NOT DISTINCT FROM NEW.security_stamp + UNION + -- Changed set + SELECT NEW.card_id, 'set', OLD.set, NEW.set, NEW.change_set_id + WHERE OLD.set IS NOT DISTINCT FROM NEW.set + UNION + -- Changed set_id + SELECT NEW.card_id, 'set_id', CAST(OLD.set_id AS VARCHAR), CAST(NEW.set_id AS VARCHAR), NEW.change_set_id + WHERE OLD.set_id IS NOT DISTINCT FROM NEW.set_id + UNION + -- Changed set_name + SELECT NEW.card_id, 'set_name', OLD.set_name, NEW.set_name, NEW.change_set_id + WHERE OLD.set_name IS NOT DISTINCT FROM NEW.set_name + UNION + -- Changed set_search_uri + SELECT NEW.card_id, 'set_search_uri', OLD.set_search_uri, NEW.set_search_uri, NEW.change_set_id + WHERE OLD.set_search_uri IS NOT DISTINCT FROM NEW.set_search_uri + UNION + -- Changed set_type + SELECT NEW.card_id, 'set_type', OLD.set_type, NEW.set_type, NEW.change_set_id + WHERE OLD.set_type IS NOT DISTINCT FROM NEW.set_type + UNION + -- Changed set_uri + SELECT NEW.card_id, 'set_uri', OLD.set_uri, NEW.set_uri, NEW.change_set_id + WHERE OLD.set_uri IS NOT DISTINCT FROM NEW.set_uri + UNION + -- Changed story_spotlight + SELECT NEW.card_id, 'story_spotlight', OLD.story_spotlight::TEXT, NEW.story_spotlight::TEXT, NEW.change_set_id + WHERE OLD.story_spotlight IS NOT DISTINCT FROM NEW.story_spotlight + UNION + -- Changed tcgplayer_id + SELECT NEW.card_id, 'tcgplayer_id', CAST(OLD.tcgplayer_id AS VARCHAR), CAST(NEW.tcgplayer_id AS VARCHAR), NEW.change_set_id + WHERE OLD.tcgplayer_id IS NOT DISTINCT FROM NEW.tcgplayer_id + UNION + -- Changed textless + SELECT NEW.card_id, 'textless', OLD.textless::TEXT, NEW.textless::TEXT, NEW.change_set_id + WHERE OLD.textless IS NOT DISTINCT FROM NEW.textless + UNION + -- Changed toughness + SELECT NEW.card_id, 'toughness', OLD.toughness, NEW.toughness, NEW.change_set_id + WHERE OLD.toughness IS NOT DISTINCT FROM NEW.toughness + UNION + -- Changed type_line + SELECT NEW.card_id, 'type_line', OLD.type_line, NEW.type_line, NEW.change_set_id + WHERE OLD.type_line IS NOT DISTINCT FROM NEW.type_line + UNION + -- Changed uri + SELECT NEW.card_id, 'uri', OLD.uri, NEW.uri, NEW.change_set_id + WHERE OLD.uri IS NOT DISTINCT FROM NEW.uri + UNION + -- Changed uri_edhrec + SELECT NEW.card_id, 'uri_edhrec', OLD.uri_edhrec, NEW.uri_edhrec, NEW.change_set_id + WHERE OLD.uri_edhrec IS NOT DISTINCT FROM NEW.uri_edhrec + UNION + -- Changed uri_gatherer + SELECT NEW.card_id, 'uri_gatherer', OLD.uri_gatherer, NEW.uri_gatherer, NEW.change_set_id + WHERE OLD.uri_gatherer IS NOT DISTINCT FROM NEW.uri_gatherer + UNION + -- Changed uri_tcgplayer_infinite_articles + SELECT NEW.card_id, 'uri_tcgplayer_infinite_articles', OLD.uri_tcgplayer_infinite_articles, NEW.uri_tcgplayer_infinite_articles, NEW.change_set_id + WHERE OLD.uri_tcgplayer_infinite_articles IS NOT DISTINCT FROM NEW.uri_tcgplayer_infinite_articles + UNION + -- Changed uri_tcgplayer_infinite_decks + SELECT NEW.card_id, 'uri_tcgplayer_infinite_decks', OLD.uri_tcgplayer_infinite_decks, NEW.uri_tcgplayer_infinite_decks, NEW.change_set_id + WHERE OLD.uri_tcgplayer_infinite_decks IS NOT DISTINCT FROM NEW.uri_tcgplayer_infinite_decks + UNION + -- Changed variation + SELECT NEW.card_id, 'variation', OLD.variation::TEXT, NEW.variation::TEXT, NEW.change_set_id + WHERE OLD.variation IS NOT DISTINCT FROM NEW.variation + UNION + -- Changed active + SELECT NEW.card_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Card + BEFORE INSERT ON tcg.public.TCG_MTG_Card + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Card() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Card + BEFORE UPDATE ON tcg.public.TCG_MTG_Card + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Card() +; \ No newline at end of file diff --git a/sql/31150_tri_TCG_MTG_Inventory.sql b/sql/31150_tri_TCG_MTG_Inventory.sql new file mode 100644 index 0000000..8885dea --- /dev/null +++ b/sql/31150_tri_TCG_MTG_Inventory.sql @@ -0,0 +1,164 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Inventory() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Inventory() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Inventory_Audit ( + inventory_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed card_id + SELECT NEW.inventory_id, 'card_id', CAST(OLD.card_id AS VARCHAR), CAST(NEW.card_id AS VARCHAR), NEW.change_set_id + WHERE OLD.card_id IS NOT DISTINCT FROM NEW.card_id + UNION + -- Changed finish_id + SELECT NEW.inventory_id, 'finish_id', CAST(OLD.finish_id AS VARCHAR), CAST(NEW.finish_id AS VARCHAR), NEW.change_set_id + WHERE OLD.finish_id IS NOT DISTINCT FROM NEW.finish_id + UNION + -- Changed condition_id + SELECT NEW.inventory_id, 'condition_id', CAST(OLD.condition_id AS VARCHAR), CAST(NEW.condition_id AS VARCHAR), NEW.change_set_id + WHERE OLD.condition_id IS NOT DISTINCT FROM NEW.condition_id + UNION + -- Changed sleeve_colour_name + SELECT NEW.inventory_id, 'sleeve_colour_name', OLD.sleeve_colour_name, NEW.sleeve_colour_name, NEW.change_set_id + WHERE OLD.sleeve_colour_name IS NOT DISTINCT FROM NEW.sleeve_colour_name + UNION + -- Changed location_name + SELECT NEW.inventory_id, 'location_name', OLD.location_name, NEW.location_name, NEW.change_set_id + WHERE OLD.location_name IS NOT DISTINCT FROM NEW.location_name + UNION + -- Changed acquired_from + SELECT NEW.inventory_id, 'acquired_from', OLD.acquired_from, NEW.acquired_from, NEW.change_set_id + WHERE OLD.acquired_from IS NOT DISTINCT FROM NEW.acquired_from + UNION + -- Changed acquired_on + SELECT NEW.inventory_id, 'acquired_on', TO_CHAR(OLD.acquired_on, 'YYYY-MM-DD'), TO_CHAR(NEW.acquired_on, 'YYYY-MM-DD'), NEW.change_set_id + WHERE OLD.acquired_on IS NOT DISTINCT FROM NEW.acquired_on + UNION + -- Changed cost_gbp + SELECT NEW.inventory_id, 'cost_gbp', CAST(OLD.cost_gbp AS VARCHAR), CAST(NEW.cost_gbp AS VARCHAR), NEW.change_set_id + WHERE OLD.cost_gbp IS NOT DISTINCT FROM NEW.cost_gbp + UNION + -- Changed sale_price_gbp + SELECT NEW.inventory_id, 'sale_price_gbp', CAST(OLD.sale_price_gbp AS VARCHAR), CAST(NEW.sale_price_gbp AS VARCHAR), NEW.change_set_id + WHERE OLD.sale_price_gbp IS NOT DISTINCT FROM NEW.sale_price_gbp + UNION + -- Changed is_sold + SELECT NEW.inventory_id, 'is_sold', OLD.is_sold::TEXT, NEW.is_sold::TEXT, NEW.change_set_id + WHERE OLD.is_sold IS NOT DISTINCT FROM NEW.is_sold + UNION + -- Changed is_destroyed + SELECT NEW.inventory_id, 'is_destroyed', OLD.is_destroyed::TEXT, NEW.is_destroyed::TEXT, NEW.change_set_id + WHERE OLD.is_destroyed IS NOT DISTINCT FROM NEW.is_destroyed + UNION + -- Changed notes + SELECT NEW.inventory_id, 'notes', OLD.notes, NEW.notes, NEW.change_set_id + WHERE OLD.notes IS NOT DISTINCT FROM NEW.notes + UNION + -- Changed alterations_customisations + SELECT NEW.inventory_id, 'alterations_customisations', OLD.alterations_customisations, NEW.alterations_customisations, NEW.change_set_id + WHERE OLD.alterations_customisations IS NOT DISTINCT FROM NEW.alterations_customisations + UNION + -- Changed grading_company_name + SELECT NEW.inventory_id, 'grading_company_name', OLD.grading_company_name, NEW.grading_company_name, NEW.change_set_id + WHERE OLD.grading_company_name IS NOT DISTINCT FROM NEW.grading_company_name + UNION + -- Changed grading_score + SELECT NEW.inventory_id, 'grading_score', OLD.grading_score, NEW.grading_score, NEW.change_set_id + WHERE OLD.grading_score IS NOT DISTINCT FROM NEW.grading_score + UNION + -- Changed subgrades + SELECT NEW.inventory_id, 'subgrades', OLD.subgrades, NEW.subgrades, NEW.change_set_id + WHERE OLD.subgrades IS NOT DISTINCT FROM NEW.subgrades + UNION + -- Changed misprint_errors + SELECT NEW.inventory_id, 'misprint_errors', OLD.misprint_errors, NEW.misprint_errors, NEW.change_set_id + WHERE OLD.misprint_errors IS NOT DISTINCT FROM NEW.misprint_errors + UNION + -- Changed miscut_errors + SELECT NEW.inventory_id, 'miscut_errors', OLD.miscut_errors, NEW.miscut_errors, NEW.change_set_id + WHERE OLD.miscut_errors IS NOT DISTINCT FROM NEW.miscut_errors + UNION + -- Changed playability + SELECT NEW.inventory_id, 'playability', OLD.playability, NEW.playability, NEW.change_set_id + WHERE OLD.playability IS NOT DISTINCT FROM NEW.playability + UNION + -- Changed owner_user_id + SELECT NEW.inventory_id, 'owner_user_id', CAST(OLD.owner_user_id AS VARCHAR), CAST(NEW.owner_user_id AS VARCHAR), NEW.change_set_id + WHERE OLD.owner_user_id IS NOT DISTINCT FROM NEW.owner_user_id + UNION + -- Changed ownership_status_name + SELECT NEW.inventory_id, 'ownership_status_name', OLD.ownership_status_name, NEW.ownership_status_name, NEW.change_set_id + WHERE OLD.ownership_status_name IS NOT DISTINCT FROM NEW.ownership_status_name + UNION + -- Changed trading_status_name + SELECT NEW.inventory_id, 'trading_status_name', OLD.trading_status_name, NEW.trading_status_name, NEW.change_set_id + WHERE OLD.trading_status_name IS NOT DISTINCT FROM NEW.trading_status_name + UNION + -- Changed loaned_to_user_id + SELECT NEW.inventory_id, 'loaned_to_user_id', CAST(OLD.loaned_to_user_id AS VARCHAR), CAST(NEW.loaned_to_user_id AS VARCHAR), NEW.change_set_id + WHERE OLD.loaned_to_user_id IS NOT DISTINCT FROM NEW.loaned_to_user_id + UNION + -- Changed acquired_on + SELECT NEW.inventory_id, 'acquired_on', TO_CHAR(OLD.acquired_on, 'YYYY-MM-DD'), TO_CHAR(NEW.acquired_on, 'YYYY-MM-DD'), NEW.change_set_id + WHERE OLD.acquired_on IS NOT DISTINCT FROM NEW.acquired_on + UNION + -- Changed acquired_on + SELECT NEW.inventory_id, 'acquired_on', TO_CHAR(OLD.acquired_on, 'YYYY-MM-DD'), TO_CHAR(NEW.acquired_on, 'YYYY-MM-DD'), NEW.change_set_id + WHERE OLD.acquired_on IS NOT DISTINCT FROM NEW.acquired_on + UNION + -- Changed provenance + SELECT NEW.inventory_id, 'provenance', OLD.provenance, NEW.provenance, NEW.change_set_id + WHERE OLD.provenance IS NOT DISTINCT FROM NEW.provenance + UNION + -- Changed signed_by_names + SELECT NEW.inventory_id, 'signed_by_names', OLD.signed_by_names, NEW.signed_by_names, NEW.change_set_id + WHERE OLD.signed_by_names IS NOT DISTINCT FROM NEW.signed_by_names + UNION + -- Changed signature_condition_name + SELECT NEW.inventory_id, 'signature_condition_name', OLD.signature_condition_name, NEW.signature_condition_name, NEW.change_set_id + WHERE OLD.signature_condition_name IS NOT DISTINCT FROM NEW.signature_condition_name + UNION + -- Changed active + SELECT NEW.inventory_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Inventory + BEFORE INSERT ON tcg.public.TCG_MTG_Inventory + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Inventory() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Inventory + BEFORE UPDATE ON tcg.public.TCG_MTG_Inventory + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Inventory() +; \ No newline at end of file diff --git a/sql/31196_tri_TCG_MTG_Deck_Commander_Bracket.sql b/sql/31196_tri_TCG_MTG_Deck_Commander_Bracket.sql new file mode 100644 index 0000000..b48234b --- /dev/null +++ b/sql/31196_tri_TCG_MTG_Deck_Commander_Bracket.sql @@ -0,0 +1,64 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Commander_Bracket() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Commander_Bracket() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Deck_Commander_Bracket_Audit ( + commander_bracket_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed name + SELECT NEW.commander_bracket_id, 'name', CAST(OLD.name AS VARCHAR), CAST(NEW.name AS VARCHAR), NEW.change_set_id + WHERE OLD.name IS NOT DISTINCT FROM NEW.name + UNION + -- Changed description + SELECT NEW.commander_bracket_id, 'description', CAST(OLD.description AS VARCHAR), CAST(NEW.description AS VARCHAR), NEW.change_set_id + WHERE OLD.description IS NOT DISTINCT FROM NEW.description + UNION + -- Changed display_order + SELECT NEW.commander_bracket_id, 'display_order', CAST(OLD.display_order AS VARCHAR), CAST(NEW.display_order AS VARCHAR), NEW.change_set_id + WHERE OLD.display_order IS NOT DISTINCT FROM NEW.display_order + UNION + -- Changed active + SELECT NEW.commander_bracket_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Deck_Commander_Bracket + BEFORE INSERT ON tcg.public.TCG_MTG_Deck_Commander_Bracket + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Commander_Bracket() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Deck_Commander_Bracket + BEFORE UPDATE ON tcg.public.TCG_MTG_Deck_Commander_Bracket + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Commander_Bracket() +; \ No newline at end of file diff --git a/sql/31200_tri_TCG_MTG_Deck.sql b/sql/31200_tri_TCG_MTG_Deck.sql new file mode 100644 index 0000000..cbf2309 --- /dev/null +++ b/sql/31200_tri_TCG_MTG_Deck.sql @@ -0,0 +1,64 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Deck_Audit ( + deck_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed name + SELECT NEW.deck_id, 'name', CAST(OLD.name AS VARCHAR), CAST(NEW.name AS VARCHAR), NEW.change_set_id + WHERE OLD.name IS NOT DISTINCT FROM NEW.name + UNION + -- Changed is_commander + SELECT NEW.deck_id, 'is_commander', OLD.is_commander::TEXT, NEW.is_commander::TEXT, NEW.change_set_id + WHERE OLD.is_commander IS NOT DISTINCT FROM NEW.is_commander + UNION + -- Changed commander_bracket_id + SELECT NEW.deck_id, 'commander_bracket_id', CAST(OLD.commander_bracket_id AS VARCHAR), CAST(NEW.commander_bracket_id AS VARCHAR), NEW.change_set_id + WHERE OLD.commander_bracket_id IS NOT DISTINCT FROM NEW.commander_bracket_id + UNION + -- Changed active + SELECT NEW.deck_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Deck + BEFORE INSERT ON tcg.public.TCG_MTG_Deck + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Deck + BEFORE UPDATE ON tcg.public.TCG_MTG_Deck + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck() +; \ No newline at end of file diff --git a/sql/31204_tri_TCG_MTG_Deck_Inventory_Link.sql b/sql/31204_tri_TCG_MTG_Deck_Inventory_Link.sql new file mode 100644 index 0000000..31dca42 --- /dev/null +++ b/sql/31204_tri_TCG_MTG_Deck_Inventory_Link.sql @@ -0,0 +1,64 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Inventory_Link() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Inventory_Link() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Deck_Inventory_Link_Audit ( + link_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed deck_id + SELECT NEW.link_id, 'deck_id', CAST(OLD.deck_id AS VARCHAR), CAST(NEW.deck_id AS VARCHAR), NEW.change_set_id + WHERE OLD.deck_id IS NOT DISTINCT FROM NEW.deck_id + UNION + -- Changed inventory_id + SELECT NEW.link_id, 'inventory_id', CAST(OLD.inventory_id AS VARCHAR), CAST(NEW.inventory_id AS VARCHAR), NEW.change_set_id + WHERE OLD.inventory_id IS NOT DISTINCT FROM NEW.inventory_id + UNION + -- Changed display_order + SELECT NEW.link_id, 'display_order', CAST(OLD.display_order AS VARCHAR), CAST(NEW.display_order AS VARCHAR), NEW.change_set_id + WHERE OLD.display_order IS NOT DISTINCT FROM NEW.display_order + UNION + -- Changed active + SELECT NEW.link_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Deck_Inventory_Link + BEFORE INSERT ON tcg.public.TCG_MTG_Deck_Inventory_Link + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Inventory_Link() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Deck_Inventory_Link + BEFORE UPDATE ON tcg.public.TCG_MTG_Deck_Inventory_Link + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Inventory_Link() +; \ No newline at end of file diff --git a/sql/90000_usp_populate.sql b/sql/90000_usp_populate.sql new file mode 100644 index 0000000..34483df --- /dev/null +++ b/sql/90000_usp_populate.sql @@ -0,0 +1,566 @@ + +-- Change Set +INSERT INTO tcg.public.TCG_Change_Set ( + comment + , updated_last_by_user_id +) +VALUES ( + 'Initial population of lookup tables.' + , 3 +); + +-- User +INSERT INTO tcg.public.TCG_User ( + firstname + , surname + , email + , is_super_user + , change_set_id +) +VALUES ( + 'Teddy' + , 'Smith' + , 'edward.middletonsmith@gmail.com' + , TRUE + , 1 +); + +INSERT INTO tcg.public.TCG_Change_Set ( + comment + , updated_last_by_user_id +) +VALUES ( + 'Alter created by user id in User table from initial upload.' + , 3 +); + +UPDATE tcg.public.TCG_User +SET + created_by_user_id = 3 + , updated_last_by_user_id = 3 + , change_set_id = 4 +; + +-- Condition +INSERT INTO tcg.public.TCG_Condition ( + name + , code + , description + , summary + , us_name + , change_set_id + , created_by_user_id + , updated_last_by_user_id +) +VALUES + ( + 'Mint' -- name + , 'M' -- code + , $$A mint card is in perfect condition; no excuses. This means, that the front is in perfect condition, there are no scratches on the surface, and the surface is perfectly clean. For the back it means, that the card is indistinguishable from cards of a newly openend booster. If a card has a signature or a Grand Prix stamp it can never be graded Mint, even if the card stock is otherwise in Mint condition. +In most cases it doesn't make sense to grade a card as Mint. For newer cards, the value of a Mint card is basically the same as a Near Mint card. Older cards (1993-96), however, may command a high premium if they are in actual Mint condition - usually professionally graded. Thus 'Mint' is mainly a grading for cards where there is a high collectors' interest or a high professional grade. For cards that are sold mostly for playing purpose, a Near Mint grade is a safer choice.$$ -- description + , 'Perfect' -- summary + , 'Mint' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Near Mint' -- name + , 'NM' -- code + , $$A Near Mint card looks like it has never been played without sleeves. Small allowances can be made, but the card generally shows no wear. +The border of NM card can have small white spots, but they must be very few and very small. When the card is inspected under bright daylight, the surface must generally appear clean. It can have a few minor spots, but scratches can never be allowed for NM cards. +Generally a Near Mint card is in a condition that would make it considered unmarked if played in an unsleeved deck. (Not recommended!) +As the Mint grade is often not used for cards of newer expansions, Near Mint usually means Near Mint or better (equivalent to the American NM/M grade).$$ -- description + , 'Booster-Fresh' -- summary + , 'Near Mint' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Excellent' -- name + , 'EX' -- code + , $$An Excellent cards look like it was used for a few games without sleeves. For Excellent cards it is almost always clearly visible upon first inspection that the card is not in perfect condition. However, although the damage is clearly visible it is only of minor severity. +Excellent cards usually have a couple of white spots at the corners or around the border. The surface may have minor scratches, that are visible upon closer inspection. However, the card cannot be graded Excellent if the creases are so deep that they are visible upon first sight. +An Excellent card is usually in a condition where it is not quite clear if the card would be considered marked or unmarked if it would be played in a tournament without sleeves. +The American equivalent usually is Slightly Played or Lightly Played (not to be confused with the European Light Played).$$ -- description + , 'Minor-Wear' -- summary + , 'Slightly Played' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Good' -- name + , 'GD' -- code + , $$A Good card looks like it might have been used for a long tournament without sleeves. +Cards in Good condition usually show strong wear all around the card. The edges and corners have many white spots, the surface usually has scratches and the card usually has accumulated some dirt on its surface. However, the card still only has damage that stems from regular play. The card has no water damage or bends whatsoever. +A Good card (and all cards in worse condition) are clearly in a condition that would make them ineligible for play without sleeves as they would be considered marked. +The American equivalent to this is usually 'Moderately Played' or 'Very Good'. Note that 'Good' is a bit of a misnomer. A Good card doesn't really look good. In fact it looks pretty beat up, making the American Very Good even more of a misnomer.$$ -- description + , 'Visible-Wear' -- summary + , 'Moderately Played' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Light Played' -- name + , 'LP' -- code + , $$A Light Played card looks as if it has been used without sleeves for an extended period of time. +A Light Played card is clearly legal for play in a sleeved deck. It has also not been tampered with (inked border, random scribblings on the card etc.). If both of these criteria apply the card may look very bad, but it can be graded Light Played. +The American equivalent usually is 'Played' or 'Good'.$$ -- description + , 'Severe-Wear' -- summary + , 'Played' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Played' -- name + , 'PL' -- code + , $$A Played card looks as bad as you can get a card through regular use without sleeves. +A Played card looks extremely bad, and it is doubtful if the card is tournament legal even in a sleeved deck. However, the card has not been tampered with otherwise (inked border, random scribblings on the card etc.). +The American equivalent usually is Heavily Played or Good.$$ -- description + , 'Damaged' -- summary + , 'Heavily Played' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Poor' -- name + , 'PO' -- code + , $$A Poor card has damage that cannot normally have stemmed from regular use of the card. +A card in Poor condition is literally destroyed. It is either obviously illegal for tournament play or has been tampered with in ways that destroy its worth almost completely (inked border, random scribblings on the card etc.). +Americans usually use Poor in the same way.$$ -- description + , 'Destroyed' -- summary + , 'Poor' -- us_name + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) +; + +' + +-- MTG Finish +INSERT INTO tcg.public.TCG_MTG_Finish ( + name + , description + , change_set_id + , created_by_user_id + , updated_last_by_user_id +) +VALUES + ( + 'Standard' -- name + , '' -- description + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Foil' -- name + , '' -- description + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Etched Foil' -- name + , '' -- description + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Borderless' -- name + , '' -- description + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Extended Art' -- name + , '' -- description + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) + , ( + 'Showcase' -- name + , '' -- description + , 1 -- change_set_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + ) +; + +-- Inventory +SELECT I_T.temp_id -- COUNT(*) + , I_T.name + , MAIN_CARD.name + , I_T.set_code + , MAIN_CARD.SET + , I_T.collector_number + , MAIN_CARD.collector_number + , MAIN_CARD.set_type + , I_T.display_order + , I_T.token_rear_side_name + , REAR_CARD.name AS rear_name + , I_T.token_rear_side_set_code + , REAR_CARD.SET AS rear_set + , I_T.token_rear_side_collector_number + , REAR_CARD.collector_number AS rear_collector_number + , REAR_CARD.set_type AS rear_set_type +FROM tcg.public.TCG_MTG_Inventory_Temp I_T +LEFT JOIN tcg.public.TCG_MTG_Card MAIN_CARD + ON ( + ( + I_T.is_token = FALSE + AND LOWER(I_T.set_code) = MAIN_CARD.SET + ) + OR ( + I_T.is_token = TRUE + AND MAIN_CARD.set_type = 'token' + AND 't' || LOWER(I_T.set_code) = MAIN_CARD.set + ) + /* + OR ( + I_T.set_code LIKE 'FIN%' + AND C.set = 'fin' + ) + */ + ) + AND I_T.collector_number = MAIN_CARD.collector_number +LEFT JOIN tcg.public.TCG_MTG_Card REAR_CARD + ON ( + ( + I_T.is_token = FALSE + AND LOWER(I_T.set_code) = REAR_CARD.SET + ) + OR ( + I_T.is_token = TRUE + AND REAR_CARD.set_type = 'token' + AND 't' || LOWER(I_T.set_code) = REAR_CARD.set + ) + ) + AND I_T.collector_number = REAR_CARD.collector_number +WHERE -- 1=1 OR + + MAIN_CARD.card_id IS NULL + -- OR C.name <> I_T.name + -- OR C.set_type = 'token' + OR ( + I_T.is_token = TRUE + AND REAR_CARD.card_id IS NULL + ) + -- I_T.name = 'Idyllic Beachfront' +-- GROUP BY I_T.temp_id +ORDER BY I_T.display_order +; + +SELECT -- * + C.name + , C.set + , C.collector_number + , * +FROM tcg.public.TCG_MTG_Card C +WHERE +-- C.name LIKE 'Dragon Illusion' + C.set = 'tdm' + AND C.collector_number = '294' +ORDER BY C.set, C.collector_number +; + + +INSERT INTO tcg.public.TCG_MTG_Inventory ( + card_id + , finish_id + , condition_id + , sleeve_colour_name + , location_name + , acquired_from + , acquired_on + , cost_gbp + , sale_price_gbp + , is_sold + , is_destroyed + , notes + , alterations_customisations + , grading_company_name + , grading_score + , subgrades + , misprint_errors + , miscut_errors + , playability + , owner_user_id + , ownership_status_name + , trading_status_name + , loaned_to_user_id + , loan_start_on + , loan_end_on + , provenance + , signed_by_names + , signature_condition_name + , active + , created_by_user_id + , updated_last_by_user_id + , change_set_id + + , token_rear_side_card_id +) +SELECT + MAIN_CARD.card_id + , I_T.finish_id + , I_T.condition_id + , I_T.sleeve_colour_name + , I_T.location_name + , I_T.acquired_from + , I_T.acquired_on + , I_T.cost_gbp + , I_T.sale_price_gbp + , I_T.is_sold + , I_T.is_destroyed + , I_T.notes + , I_T.alterations_customisations + , I_T.grading_company_name + , I_T.grading_score + , I_T.subgrades + , I_T.misprint_errors + , I_T.miscut_errors + , I_T.playability + , I_T.owner_user_id + , I_T.ownership_status_name + , I_T.trading_status_name + , I_T.loaned_to_user_id + , I_T.loan_start_on + , I_T.loan_end_on + , I_T.provenance + , I_T.signed_by_names + , I_T.signature_condition_name + , I_T.active + , I_T.created_by_user_id + , I_T.updated_last_by_user_id + , I_T.change_set_id + + , REAR_CARD.card_id +FROM tcg.public.TCG_MTG_Inventory_Temp I_T +INNER JOIN tcg.public.TCG_MTG_Card MAIN_CARD + ON ( + ( + I_T.is_token = FALSE + AND LOWER(I_T.set_code) = MAIN_CARD.SET + ) + OR ( + I_T.is_token = TRUE + AND MAIN_CARD.set_type = 'token' + AND 't' || LOWER(I_T.set_code) = MAIN_CARD.set + ) + /* + OR ( + I_T.set_code LIKE 'FIN%' + AND C.set = 'fin' + ) + */ + ) + AND I_T.collector_number = MAIN_CARD.collector_number +LEFT JOIN tcg.public.TCG_MTG_Card REAR_CARD + ON ( + ( + I_T.is_token = FALSE + AND LOWER(I_T.set_code) = REAR_CARD.SET + ) + OR ( + I_T.is_token = TRUE + AND REAR_CARD.set_type = 'token' + AND 't' || LOWER(I_T.set_code) = REAR_CARD.set + ) + ) + AND I_T.collector_number = REAR_CARD.collector_number +ORDER BY I_T.display_order +; +-- SELECT * FROM tcg.public.TCG_MTG_Inventory_Temp; +SELECT + -- I.inventory_id + C.card_id + , C.name + , C.card_faces +FROM /* tcg.public.TCG_MTG_Inventory I +INNER JOIN*/ tcg.public.TCG_MTG_Card C -- ON I.card_id = C.card_id +WHERE + -- C.name LIKE '%Yuna%' + C.set_type = 'token' + -- AND C.card_faces LIKE '%name'': ''Treasure' + AND C.name IN ('Treasure', 'Clue') +ORDER BY C.card_id -- I.inventory_id +; + +SELECT + C.set + , C.collector_number + , COUNT(*) +FROM tcg.public.TCG_MTG_Card C +GROUP BY + C.set + , C.collector_number +HAVING COUNT(*) > 1 +; + + + + +-- Deck Lookup Tables +INSERT INTO tcg.public.TCG_MTG_Deck_Commander_Bracket ( + name + , description + , display_order + , created_by_user_id + , updated_last_by_user_id + , change_set_id +) +VALUES + ( + 'Exhibition' -- name + , $$Throw down with your ultra-casual Commander deck! + +Winning is not the primary goal here, as it's more about showing off something unusual you've made. Villains yelling in the art? Everything has the number four? Oops, all Horses? Those are all fair game! The games here are likely to go long and end slowly. + +Just focus on having fun and enjoying what the table has brought! + +Deck Building: No cards from the Game Changers list. No intentional two-card infinite combos, mass land denial, or extra-turn cards. Tutors should be sparse.$$ -- description + , 1 + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) + , ( + 'Core' -- name + , $$The easiest reference point is that the average current preconstructed deck is at a Core (Bracket 2) level. + +While Bracket 2 decks may not have every perfect card, they have the potential for big, splashy turns, strong engines, and are built in a way that works toward winning the game. While the game is unlikely to end out of nowhere and generally goes nine or more turns, you can expect big swings. The deck usually has some cards that aren't perfect from a gameplay perspective but are there for flavor reasons, or just because they bring a smile to your face. + +Deck Building: No cards from the Game Changers list. No intentional two-card infinite combos or mass land denial. Extra-turn cards should only appear in low quantities and are not intended to be chained in succession or looped. Tutors should be sparse.$$ -- description + , 2 + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) + , ( + 'Upgraded' -- name + , $$These decks are souped up and ready to play beyond the strength of an average preconstructed deck. + +They are full of carefully selected cards, with work having gone into figuring out the best card for each slot. The games tend to be a little faster as well, ending a turn or two sooner than your Core (Bracket 2) decks. This also is where players can begin playing up to three cards from the Game Changers list, amping up the decks further. Of course, it doesn't have to have any Game Changers to be a Bracket 3 deck: many decks are more powerful than a preconstructed deck, even without them! + +These decks should generally not have any two-card infinite combos that can happen cheaply and in about the first six or so turns of the game, but it's possible the long game could end with one being deployed, even out of nowhere. + +Deck Building: Up to three cards from the Game Changers list. No intentional early-game two-card infinite combos. Extra-turn cards should only appear in low quantities and are not intended to be chained in succession or looped. No mass land denial.$$ -- description + , 3 + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) + , ( + 'Optimized' -- name + , $$It's time to go wild! + +Bring out your strongest decks and cards. You can expect to see explosive starts, strong tutors, cheap combos that end games, mass land destruction, or a deck full of cards off the Game Changers list. This is high-powered Commander, and games have the potential to end quickly. + +The focus here is on bringing the best version of the deck you want to play, but not one built around a tournament metagame. It's about shuffling up your strong and fully optimized deck, whatever it may be, and seeing how it fares. For most Commander players, these are the highest-power Commander decks you will interact with. + +Deck Building: There are no restrictions (other than the banned list).$$ -- description + , 4 + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) + , ( + 'cEDh' -- name + , $$This is high power with a very competitive and metagame-focused mindset. + +"Mindset" is a key part of that description: Much of it is in how you approach the format and deck building. It's not just no holds barred, where you play your most powerful cards like in Bracket 4. It requires careful planning: There is care paid into following and paying attention to a metagame and tournament structure, and no sacrifices are made in deck building as you try to be the one to win the pod. Additionally, there is special care and attention paid to behavior and tableside negotiation (such as not making spite plays or concessions) that play into the tournament structure. + +cEDH, or "competitive Commander" and similar names, is where winning matters more than self-expression. You might not be playing your favorite cards or commanders, as pet cards are usually replaced with cards needed in the meta, but you're playing what you think will be most likely to win. + +Deck Building: There are no restrictions (other than the banned list). +$$ -- description + , 5 + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) +; + +' + +INSERT INTO tcg.public.TCG_MTG_Deck ( + name + , commander_bracket_id + , created_by_user_id + , updated_last_by_user_id + , change_set_id +) +VALUES + ( + 'Final Fantasy: Counter Blitz' -- name + , 2 -- commander_bracket_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) + , ( + 'Tarkir Dragonstorm: Jeskai Striker' -- name + , 2 -- commander_bracket_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) + , ( + 'The Lost Caverns of Ixalan: Blood Rites' -- name + , 2 -- commander_bracket_id + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id + ) +; + + +SELECT * +FROM tcg.public.TCG_MTG_Deck D +; + +INSERT INTO tcg.public.TCG_MTG_Deck_Inventory_Link ( + deck_id + , inventory_id + , display_order + , created_by_user_id + , updated_last_by_user_id + , change_set_id +) +SELECT + CASE WHEN C.set LIKE '%fic' OR C.set LIKE '%fin' THEN 1 ELSE 2 END AS deck_id + , I.inventory_id + , CASE WHEN C.set LIKE '%fic' OR C.set LIKE '%fin' THEN I.inventory_id ELSE I.inventory_id - 106 END AS display_order + , 3 -- created_by_user_id + , 3 -- updated_last_by_user_id + , 1 -- change_set_id +FROM tcg.public.TCG_MTG_Inventory I +INNER JOIN tcg.public.TCG_MTG_Card C ON I.card_id = C.card_id +ORDER BY I.inventory_id +; + +INSERT INTO tcg.public.TCG_Change_Set ( + comment + , updated_last_by_user_id +) +VALUES ( + 'Remove extra cards from Tarkir Dragonstorm deck.' + , 3 +); + +UPDATE tcg.public.TCG_MTG_Deck_Inventory_Link DIL +SET + active = FALSE + , change_set_id = 6 +WHERE DIL.inventory_id IN (217, 218) +; \ No newline at end of file diff --git a/sql/99150_qry_unowned_land.sql b/sql/99150_qry_unowned_land.sql new file mode 100644 index 0000000..a8f82ae --- /dev/null +++ b/sql/99150_qry_unowned_land.sql @@ -0,0 +1,93 @@ +-- Unowned Land + +WITH + Cleaned_Card AS ( + SELECT + C.card_id + , C.name + , C.type_line + , C.oracle_text + , C.color_identity + , C.produced_mana + , COALESCE(C.price_eur, C.price_usd, C.price_tix) AS price + FROM tcg.public.TCG_MTG_Card C + ) + , Owned_Card AS ( + SELECT C.name + FROM tcg.public.TCG_MTG_Card C + INNER JOIN tcg.public.TCG_MTG_Inventory I ON C.card_id = I.card_id + ) + , Unowned_Land AS ( + SELECT + CC.card_id + , CC.name + , ROW_NUMBER() OVER ( + PARTITION BY + CC.name + , CC.oracle_text + ORDER BY + CC.price NULLS LAST + , CC.type_line + , CC.card_id + ) AS index_card_price_in_card_reprints + FROM Cleaned_Card CC + LEFT JOIN Owned_Card OC ON CC.name = OC.name + WHERE + OC.name IS NULL + AND CC.type_line LIKE '%Land%' + ) +SELECT + -- COUNT(*) + -- MAX(COALESCE(CC.price, 0.0)) + CC.name + , CC.type_line + , REPLACE(CC.name, ' ', '+') + , CC.oracle_text + , CONCAT( + 'https://magicmadhouse.co.uk/search.php?search_query=' + , REPLACE(CC.name, ' ', '+') + , '&Filters=brand%3AMagic%253A%2520The%2520Gathering' + ) AS url_mm + , CONCAT( + 'https://www.chaoscards.co.uk/search/' + , REPLACE(CC.name, ' ', '+') + , '?sort=price_asc&in_stock=In+stock&brand=Magic+the+Gathering' + ) AS url_cc + , CONCAT( + 'https://www.bigorbitcards.co.uk/magic-the-gathering/search/' + , REPLACE(CC.name, ' ', '+') + , '/?order=product.price.asc' + ) AS url_boc + , CC.color_identity + , CC.produced_mana + , CC.price +FROM Cleaned_Card CC +INNER JOIN Unowned_Land UL ON CC.card_id = UL.card_id +WHERE + UL.index_card_price_in_card_reprints = 1 + AND ( -- Invalid colours + CC.color_identity NOT IN ('G, R', 'B, G', 'B, R', 'B, U') + ) + AND CC.oracle_text NOT LIKE '%This land enters tapped unless you control a Mount or Vehicle.%' +ORDER BY + CC.price NULLS LAST + , CC.type_line + , CC.name + , CC.card_id +-- LIMIT 250 +OFFSET 200 +; + +/* +SELECT + C.name + , C.set +FROM tcg.public.TCG_MTG_Card C +INNER JOIN tcg.public.TCG_MTG_Inventory I ON C.card_id = I.card_id +WHERE UPPER(C.type_line) LIKE '%SOLDIER%' +ORDER BY + C.set + , C.name +; +*/ + diff --git a/sql/deprecated/11005_tbl_TCG_Language.sql b/sql/deprecated/11005_tbl_TCG_Language.sql new file mode 100644 index 0000000..9089de5 --- /dev/null +++ b/sql/deprecated/11005_tbl_TCG_Language.sql @@ -0,0 +1,16 @@ +CREATE TABLE tcg.public.TCG_Language ( + language_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , name VARCHAR(250) NOT NULL + , description VARCHAR(250) NOT NULL + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_Language_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP + , updated_last_by_user_id INT + , change_set_id INT + , CONSTRAINT FK_TCG_Language_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/deprecated/11006_tbl_TCG_Language_Audit.sql b/sql/deprecated/11006_tbl_TCG_Language_Audit.sql new file mode 100644 index 0000000..34e7939 --- /dev/null +++ b/sql/deprecated/11006_tbl_TCG_Language_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_Language_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , language_id INT NOT NULL + , CONSTRAINT FK_TCG_Language_Audit_language_id + FOREIGN KEY (language_id) + REFERENCES tcg.public.TCG_Language(language_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_Language_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/deprecated/11101_tbl_TCG_MTG_Rarity.sql b/sql/deprecated/11101_tbl_TCG_MTG_Rarity.sql new file mode 100644 index 0000000..5dd7438 --- /dev/null +++ b/sql/deprecated/11101_tbl_TCG_MTG_Rarity.sql @@ -0,0 +1,19 @@ +CREATE TABLE tcg.public.TCG_MTG_Rarity ( + rarity_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , name VARCHAR(250) NOT NULL + , colour_name VARCHAR(250) NOT NULL + , price_ratio_min DECIMAL(6, 1) + , price_ratio_max DECIMAL(6, 1) + , active BOOLEAN NOT NULL DEFAULT TRUE + , created_on TIMESTAMP NOT NULL + , created_by_user_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Rarity_created_by_user_id + FOREIGN KEY (created_by_user_id) + REFERENCES tcg.public.TCG_User(user_id) + , updated_last_on TIMESTAMP + , updated_last_by_user_id INT + , change_set_id INT + , CONSTRAINT FK_TCG_MTG_Rarity_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/deprecated/11102_tbl_TCG_MTG_Rarity_Audit.sql b/sql/deprecated/11102_tbl_TCG_MTG_Rarity_Audit.sql new file mode 100644 index 0000000..27df744 --- /dev/null +++ b/sql/deprecated/11102_tbl_TCG_MTG_Rarity_Audit.sql @@ -0,0 +1,15 @@ + +CREATE TABLE tcg.public.TCG_MTG_Rarity_Audit ( + audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY + , rarity_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Rarity_Audit_rarity_id + FOREIGN KEY (rarity_id) + REFERENCES tcg.public.TCG_MTG_Rarity(rarity_id) + , name_field VARCHAR(255) NOT NULL + , value_prev TEXT + , value_new TEXT + , change_set_id INT NOT NULL + , CONSTRAINT FK_TCG_MTG_Rarity_Audit_change_set_id + FOREIGN KEY (change_set_id) + REFERENCES tcg.public.TCG_Change_Set(change_set_id) +); diff --git a/sql/deprecated/31101_tri_TCG_MTG_Rarity.sql b/sql/deprecated/31101_tri_TCG_MTG_Rarity.sql new file mode 100644 index 0000000..65ff3c6 --- /dev/null +++ b/sql/deprecated/31101_tri_TCG_MTG_Rarity.sql @@ -0,0 +1,68 @@ + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Rarity() +RETURNS TRIGGER AS $$ +BEGIN + NEW.created_on = CURRENT_TIMESTAMP; + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF NEW.change_set_id IS NULL THEN + RAISE EXCEPTION 'Change Set ID must be provided.'; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Rarity() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_last_on = CURRENT_TIMESTAMP; + + IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN + RAISE EXCEPTION 'New Change Set ID must be provided.'; + END IF; + + INSERT INTO tcg.public.TCG_MTG_Rarity_Audit ( + rarity_id + , name_field + , value_prev + , value_new + , change_set_id + ) + -- Changed name + SELECT NEW.rarity_id, 'name', OLD.name, NEW.name, NEW.change_set_id + WHERE OLD.name IS NOT DISTINCT FROM NEW.name + UNION + -- Changed colour_name + SELECT NEW.rarity_id, 'colour_name', OLD.colour_name, NEW.colour_name, NEW.change_set_id + WHERE OLD.colour_name IS NOT DISTINCT FROM NEW.colour_name + UNION + -- Changed price_ratio_min + SELECT NEW.rarity_id, 'price_ratio_min', CAST(OLD.price_ratio_min AS VARCHAR), CAST(NEW.price_ratio_min AS VARCHAR), NEW.change_set_id + WHERE OLD.price_ratio_min IS NOT DISTINCT FROM NEW.price_ratio_min + UNION + -- Changed price_ratio_max + SELECT NEW.rarity_id, 'price_ratio_max', CAST(OLD.price_ratio_max AS VARCHAR), CAST(NEW.price_ratio_max AS VARCHAR), NEW.change_set_id + WHERE OLD.price_ratio_max IS NOT DISTINCT FROM NEW.price_ratio_max + UNION + -- Changed active + SELECT NEW.rarity_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id + WHERE OLD.active IS NOT DISTINCT FROM NEW.active + ; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql +; + +CREATE TRIGGER TRI_before_insert_TCG_MTG_Rarity + BEFORE INSERT ON tcg.public.TCG_MTG_Rarity + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Rarity() +; +CREATE TRIGGER TRI_before_update_TCG_MTG_Rarity + BEFORE UPDATE ON tcg.public.TCG_MTG_Rarity + FOR EACH ROW + EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Rarity() +; \ No newline at end of file