diff --git a/Design/Database/exporter.py b/Design/Database/exporter.py new file mode 100755 index 0000000..937d141 --- /dev/null +++ b/Design/Database/exporter.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python +# +# -*- coding: utf-8 -*- +# +# Copyright (C) 2011 by Aevum Softwares LTDA ME +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import optparse +import os +import re + +__version__ = 0.1 +SEQUENCE_SUFFIX = "sequence" +SCHEMA = ""; + +def remove_comments(string): + string = re.sub(re.compile("/\*.*?\*/", re.DOTALL), "", string) # remove all block comments (/*COMMENT */) + string = re.sub(re.compile("//.*?\n"), "", string) # remove all single line comments (//COMMENT\n) + return string + + +def remove_lines_started_with(word, lines): + toRemove = [] + for line in lines: + if line.startswith(word): + toRemove.append(line) + for line in toRemove: + lines.remove(line) + + +def remove_lines_with(word, lines): + toRemove = [] + for line in lines: + if word in line: + toRemove.append(line) + for line in toRemove: + lines.remove(line) + + +def remove_word(word, lines, numberOfNextWords=0): + substitute = "@@@" + for i in range(len(lines)): + if not numberOfNextWords: + lines[i] = lines[i].replace(word, "") + else: + line = lines[i] + line = line.replace(word, substitute) + if substitute in line: + split = line.split() + nextWords = [] + for j in range(len(split)): + if split[j] == substitute: + for k in range(numberOfNextWords): + nextWords.append(split[k + j + 1]) + replaceString = substitute + for k in range(numberOfNextWords): + replaceString = "{0} {1}".format(replaceString, nextWords[k]) + lines[i] = line.replace(replaceString, "") + + +def put_semicolons(lines): + numberOfOpenningParenthesis = 0 + numberOfClosingParenthesis = 0 + for i in range(len(lines)): + line = lines[i] + split = lines[i].split() + if "CREATE" in split and "TABLE" in split: + if split.index("CREATE") == split.index("TABLE") - 1: + numberOfOpenningParenthesis = 0 + numberOfClosingParenthesis = 0 + + while "(" in line: + line = line.replace("(", "", 1) + numberOfOpenningParenthesis = numberOfOpenningParenthesis + 1 + + while ")" in line: + line = line.replace(")", "", 1) + numberOfClosingParenthesis = numberOfClosingParenthesis + 1 + + if numberOfOpenningParenthesis == numberOfClosingParenthesis: + if numberOfOpenningParenthesis != 0: + lines[i] = lines[i].replace("\n", "") + ";\n" + numberOfOpenningParenthesis = 0 + numberOfClosingParenthesis = 0 + + +def create_sequences(lines): + sequences = [] + lastTable = None + for i in range(len(lines)): + line = lines[i] + split = line.split() + if "CREATE" in split and "TABLE" in split: + if split.index("CREATE") == split.index("TABLE") - 1: + lastTable = split[split.index("CREATE") + 2] + elif "AUTO_INCREMENT" in line: + if "." in lastTable: + spl = lastTable.replace("\"", "").split(".") + schema = "\"" + spl[0] + "\"." + table = spl[1] + else: + table = lastTable.replace("\"", "") + schema = "" + + column = split[0].replace("\"", "") + sequences.append({"table": table, "column": column, "schema": schema}) + + for sequence in sequences: + lines.append("{0} {1}\"{2}_{3}_{4}\";\n".format( + "DROP SEQUENCE IF EXISTS", + sequence["schema"], + sequence["table"], + sequence["column"], + SEQUENCE_SUFFIX)) + lines.append("{0} {1}\"{2}_{3}_{4}\";\n".format( + "CREATE SEQUENCE ", + sequence["schema"], + sequence["table"], + sequence["column"], + SEQUENCE_SUFFIX)) + lines.append("ALTER TABLE {0}\"{1}\" ALTER COLUMN \"{2}\" SET DEFAULT NEXTVAL('{0}\"{1}_{2}_{3}\"');\n".format( + sequence["schema"], + sequence["table"], + sequence["column"], + SEQUENCE_SUFFIX,)) +def get_current_schema(lines): + for line in lines: + if "CREATE SCHEMA" in line: + return re.sub('\s+',' ',line.replace(';', '').replace('"', '')).strip().split(' ')[-1] + elif "CREATE TABLE" in line: + tbl = re.search("CREATE TABLE (.*) \(", line).group(1).replace('"', '') + if "." in tbl: + return tbl.split('.')[0].strip() + return None + +def set_schema(lines): + currSchema = get_current_schema(lines) + if currSchema == None: + print("There is no schema set on the original script! Will default to public schema") # Not implemented: add schema to all statements + else: + for i in range(len(lines)): + line = lines[i] + regex = re.compile("((" + currSchema + ")(\")?(\.|;))") + search = regex.search(line) + if search: + sufix = '' + if search.group(3): + sufix += search.group(3); + if search.group(4): + sufix += search.group(4) + + line = re.sub(regex, SCHEMA + sufix, line) + lines[i] = line + +def replace_word(word, replace, lines): + for i in range(len(lines)): + lines[i] = lines[i].replace(word, replace) + + +def replace_regex(search, replace, lines): + for i in range(len(lines)): + lines[i] = re.sub(re.compile(search), replace, lines[i]) + +def add_cascade_to_drops(lines): + for i in range(len(lines)): + line = lines[i] + if line.startswith("DROP"): + lines[i] = line.replace(";", "CASCADE;") + + +def convert(input, output): + contents = input.read() + lines = remove_comments(contents).splitlines(True) + remove_lines_with("ASC)", lines) + remove_lines_started_with("SET", lines) + remove_lines_started_with("COLLATE", lines) + remove_lines_started_with("ENGINE", lines) + remove_lines_started_with("COMMENT", lines) + remove_lines_started_with("PACK_KEYS", lines) + replace_regex("COMMENT .*,\n", ",\n", lines) + remove_word(" COMMENT ", lines) + remove_lines_started_with("USE", lines) + replace_word("`", "\"", lines) + #remove_word("'", lines) # this breaks DEFAULT '' statements + remove_word("UNSIGNED", lines) + remove_word("IF NOT EXISTS", lines) + replace_regex("CREATE SCHEMA (.*);", r"CREATE SCHEMA IF NOT EXISTS \1;", lines) + remove_word("DEFAULT CHARACTER SET =", lines, 1) + remove_word("DEFAULT CHARACTER SET", lines, 1) + remove_word("CHARACTER SET", lines, 1) + remove_word("COLLATE", lines, 1) + replace_word("DATETIME", "TIMESTAMP", lines) + replace_word("TINYINT(1)", "BOOLEAN", lines) + replace_word("LONGTEXT", "TEXT", lines) + replace_regex("INT\(\d*\)", "INT", lines) + remove_word("CONSTRAINT \"\"", lines) + remove_word("FOREIGN KEY ()", lines) + put_semicolons(lines) + add_cascade_to_drops(lines) + create_sequences(lines) + remove_word("AUTO_INCREMENT", lines) + if SCHEMA != "": + set_schema(lines); + + output.writelines(lines) + +def main(args): + """Check arguments from the command line and executed the required action""" + parser = optparse.OptionParser( + usage="Usage: %prog [options] []", + version="%prog {0}".format(__version__)) + parser.add_option("-s", "--schema", + action="store", dest="schema", + help="Schema name for the Postgre script. Default value is the table name for MySQL Workbench."); + # parser.add_option("-o", "--output", + # action="store", dest="output", + # help="Generates the output") + (options, args) = parser.parse_args() + if len(args) > 0: + input_path = args[0] + if not os.path.exists(input_path): + print("First argument should be a valid sql file") + return + + if len(args) < 2: + output_path = "{0}_postgre.{1}".format(*input_path.rsplit('.', 1)) + else: + output_path = args[1] + + if options.schema != "" and options.schema != None: + global SCHEMA + SCHEMA = options.schema + + input = open(input_path, "r") + output = open(output_path, "w") + convert(input, output) + else: + print ("Invalid parameters. You should run YYY []") + return + + +if __name__ == "__main__": + main(sys.argv) + diff --git a/Design/Database/generated_sql_file.sql b/Design/Database/generated_sql_file.sql index 7ced06a..ff02b5e 100755 --- a/Design/Database/generated_sql_file.sql +++ b/Design/Database/generated_sql_file.sql @@ -3,9 +3,9 @@ -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; -SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; +-- SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; +-- SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; +-- SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; -- ----------------------------------------------------- -- Schema youtubemd @@ -16,7 +16,7 @@ SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,N -- ----------------------------------------------------- CREATE SCHEMA IF NOT EXISTS `youtubemd` DEFAULT CHARACTER SET utf8mb4 ; SHOW WARNINGS; -USE `youtubemd` ; +-- USE `youtubemd` ; -- ----------------------------------------------------- -- Table `youtubemd`.`DownloadInformation` @@ -245,8 +245,8 @@ CREATE TABLE IF NOT EXISTS `youtubemd`.`VideoStatistics` ( ENGINE = InnoDB CHECKSUM = 1; -SHOW WARNINGS; - -SET SQL_MODE=@OLD_SQL_MODE; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; +-- SHOW WARNINGS; +-- +-- SET SQL_MODE=@OLD_SQL_MODE; +-- SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +-- SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/Design/Database/generated_sql_file_postgre.sql b/Design/Database/generated_sql_file_postgre.sql new file mode 100644 index 0000000..15c22f4 --- /dev/null +++ b/Design/Database/generated_sql_file_postgre.sql @@ -0,0 +1,235 @@ +-- MySQL Script generated by MySQL Workbench +-- jue 25 jul 2019 13:50:06 CEST +-- Model: New Model Version: 1.0 +-- MySQL Workbench Forward Engineering + +-- SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; +-- SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; +-- SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; + +-- ----------------------------------------------------- +-- Schema youtubemd +-- ----------------------------------------------------- + +-- ----------------------------------------------------- +-- Schema youtubemd +-- ----------------------------------------------------- +CREATE SCHEMA IF NOT EXISTS "youtubemd" ; +-- SHOW WARNINGS; +-- USE "youtubemd" ; +-- +-- ----------------------------------------------------- +-- Table "youtubemd"."DownloadInformation" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."DownloadInformation" ( + "file_id" VARCHAR(50) NOT NULL, + "audioQuality" ENUM('320k', '256k', '128k') NOT NULL, + "audioSampling" ENUM('44000', '48000') NULL, + "Metadata_idMetadata" INT NOT NULL, + "VideoInformation_id" VARCHAR(11) NOT NULL, + PRIMARY KEY ("file_id", "Metadata_idMetadata", "VideoInformation_id"), + CONSTRAINT "fk_DownloadInformation_Metadata1" + FOREIGN KEY ("Metadata_idMetadata") + REFERENCES "youtubemd"."Metadata" ("idMetadata") + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT "fk_DownloadInformation_VideoInformation1" + FOREIGN KEY ("VideoInformation_id") + REFERENCES "youtubemd"."VideoInformation" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."DownloadStatistics" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."DownloadStatistics" ( + "timesRequested" INT NOT NULL DEFAULT 0, + "DownloadInformation_file_id" VARCHAR(50) NOT NULL, + PRIMARY KEY ("DownloadInformation_file_id"), + CONSTRAINT "fk_DownloadStatistics_DownloadInformation1" + FOREIGN KEY ("DownloadInformation_file_id") + REFERENCES "youtubemd"."DownloadInformation" ("file_id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."History" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."History" ( + "User_id" INT NOT NULL, + "DownloadInformation_file_id" VARCHAR(50) NOT NULL, + PRIMARY KEY ("User_id", "DownloadInformation_file_id"), + CONSTRAINT "fk_History_User1" + FOREIGN KEY ("User_id") + REFERENCES "youtubemd"."User" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT "fk_History_DownloadInformation1" + FOREIGN KEY ("DownloadInformation_file_id") + REFERENCES "youtubemd"."DownloadInformation" ("file_id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."Metadata" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."Metadata" ( + "idMetadata" INT NOT NULL , + "title" VARCHAR(100) NOT NULL, + "artist" VARCHAR(60) NOT NULL, + "cover" BLOB NOT NULL, + "duration" INT NULL, + "customMetadata" TINYINT NOT NULL DEFAULT 0, + PRIMARY KEY ("idMetadata")); +CHECKSUM = 1; + +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."Playlist" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."Playlist" ( + "id" VARCHAR(60) NOT NULL, + PRIMARY KEY ("id")); +CHECKSUM = 1; + +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."Playlist_has_VideoInformation" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."Playlist_has_VideoInformation" ( + "Playlist_id" VARCHAR(60) NOT NULL, + "VideoInformation_id" VARCHAR(11) NOT NULL, + PRIMARY KEY ("Playlist_id", "VideoInformation_id"), + CONSTRAINT "fk_Playlist_has_VideoInformation_Playlist1" + FOREIGN KEY ("Playlist_id") + REFERENCES "youtubemd"."Playlist" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT "fk_Playlist_has_VideoInformation_VideoInformation1" + FOREIGN KEY ("VideoInformation_id") + REFERENCES "youtubemd"."VideoInformation" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."PlaylistStatistics" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."PlaylistStatistics" ( + "timesRequested" INT NOT NULL, + "Playlist_id" VARCHAR(60) NOT NULL, + PRIMARY KEY ("Playlist_id"), + CONSTRAINT "fk_PlaylistStatistics_Playlist1" + FOREIGN KEY ("Playlist_id") + REFERENCES "youtubemd"."Playlist" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."Preferences" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."Preferences" ( + "language" VARCHAR(3) NOT NULL DEFAULT 'en', + "audioQuality" ENUM('320k', '256k', '128k') NOT NULL DEFAULT '128k', + "audioSampling" ENUM('44000', '48000') NOT NULL DEFAULT '44000', + "sendSongLinks" TINYINT NOT NULL DEFAULT 0, + "askForMetadata" TINYINT NOT NULL DEFAULT 1, + "User_id" INT NOT NULL, + PRIMARY KEY ("User_id"), + CONSTRAINT "fk_Preferences_User" + FOREIGN KEY ("User_id") + REFERENCES "youtubemd"."User" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."User" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."User" ( + "id" INT NOT NULL DEFAULT 0, + "name" VARCHAR(45) NULL DEFAULT 'User', + "surname" VARCHAR(45) NULL, + "username" VARCHAR(45) NULL, + "lastSeen" TIMESTAMP NOT NULL, + "firstUsage" TIMESTAMP NOT NULL, + PRIMARY KEY ("id")); +CHECKSUM = 1 + +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."VideoInformation" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."VideoInformation" ( + "id" VARCHAR(11) NOT NULL, + "title" VARCHAR(100) NOT NULL, + "channel" VARCHAR(60) NOT NULL, + PRIMARY KEY ("id")); +CHECKSUM = 1; + +-- SHOW WARNINGS; +-- +-- SHOW WARNINGS; + +-- ----------------------------------------------------- +-- Table "youtubemd"."VideoStatistics" +-- ----------------------------------------------------- +CREATE TABLE "youtubemd"."VideoStatistics" ( + "timesRequested" INT NOT NULL DEFAULT 0, + "VideoInformation_id" VARCHAR(11) NOT NULL, + PRIMARY KEY ("VideoInformation_id"), + CONSTRAINT "fk_VideoStatistics_VideoInformation1" + FOREIGN KEY ("VideoInformation_id") + REFERENCES "youtubemd"."VideoInformation" ("id") + ON DELETE NO ACTION + ON UPDATE NO ACTION); +CHECKSUM = 1; + +-- SHOW WARNINGS; +-- +-- SET SQL_MODE=@OLD_SQL_MODE; +-- SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +-- SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; +DROP SEQUENCE IF EXISTS "youtubemd"."Metadata_idMetadata_sequence"; +CREATE SEQUENCE "youtubemd"."Metadata_idMetadata_sequence"; +ALTER TABLE "youtubemd"."Metadata" ALTER COLUMN "idMetadata" SET DEFAULT NEXTVAL('"youtubemd"."Metadata_idMetadata_sequence"'); +DROP SEQUENCE IF EXISTS "youtubemd"."Metadata__sequence"; +CREATE SEQUENCE "youtubemd"."Metadata__sequence"; +ALTER TABLE "youtubemd"."Metadata" ALTER COLUMN "" SET DEFAULT NEXTVAL('"youtubemd"."Metadata__sequence"'); diff --git a/Design/Database/new_database_model.mwb b/Design/Database/new_database_model.mwb new file mode 100644 index 0000000..8a3992e Binary files /dev/null and b/Design/Database/new_database_model.mwb differ diff --git a/Design/Database/new_database_model.mwb.bak b/Design/Database/new_database_model.mwb.bak new file mode 100644 index 0000000..8d2c155 Binary files /dev/null and b/Design/Database/new_database_model.mwb.bak differ diff --git a/Design/Database/psql_model.sql b/Design/Database/psql_model.sql new file mode 100644 index 0000000..7fffb89 --- /dev/null +++ b/Design/Database/psql_model.sql @@ -0,0 +1,266 @@ +-- PostgreSQL model for YouTubeMDBot application +-- Created by Javinator9889 - thu, 24 October, 2019 +-- Last modification: mon, 4 November, 2019 +-- Version 1.1 + +-- DROP schema - only for testing +-- DROP SCHEMA IF EXISTS youtubemd CASCADE; +-- DROP TYPE IF EXISTS AFORMAT; +-- DROP TYPE IF EXISTS aquality; +-- DROP TYPE IF EXISTS behaviour; + +-- Custom "enum" types +CREATE TYPE AFORMAT AS ENUM ('mp3', 'm4a', 'ogg'); +CREATE TYPE AQUALITY AS ENUM ('128k', '96k'); +CREATE TYPE BEHAVIOUR AS ENUM ('always', 'not_found', 'ask', 'never'); + +-- Create DB schema +CREATE SCHEMA IF NOT EXISTS youtubemd; + +-- --------------------------------------- +-- Table User -- +-- --------------------------------------- +CREATE TABLE IF NOT EXISTS youtubemd.User +( + "id" INT PRIMARY KEY NOT NULL, + "name" VARCHAR(45), + "tag" VARCHAR(45), + "lang" VARCHAR(2), + "first_access" date +); + +-- --------------------------------------------- +-- Table Preferences -- +-- --------------------------------------------- +CREATE TABLE IF NOT EXISTS youtubemd.Preferences +( + "audio_format" AFORMAT NOT NULL DEFAULT 'm4a', + "audio_quality" AQUALITY NOT NULL DEFAULT '128k', + "song_behaviour" BEHAVIOUR NOT NULL DEFAULT 'not_found', + "send_song_link" BOOLEAN DEFAULT False, + "user_id" INT NOT NULL, + PRIMARY KEY ("user_id"), + CONSTRAINT "fk_user_id" + FOREIGN KEY ("user_id") + REFERENCES youtubemd.User ("id") + ON DELETE CASCADE + ON UPDATE CASCADE +); + +-- ------------------------------------------ +-- Table YouTube -- +-- ------------------------------------------ +CREATE TABLE IF NOT EXISTS youtubemd.YouTube +( + "id" VARCHAR(11) UNIQUE NOT NULL, + "times_requested" INT NOT NULL DEFAULT 0, + PRIMARY KEY ("id") +); + +-- ------------------------------------------ +-- Table Metadata -- +-- ------------------------------------------ +CREATE TABLE IF NOT EXISTS youtubemd.Metadata +( + "id" BIGSERIAL NOT NULL, + "artist" VARCHAR(45) NOT NULL, + "album" VARCHAR(45) NOT NULL, + "cover" BYTEA NOT NULL, + "release_id" VARCHAR(36), + "recording_id" VARCHAR(36), + "duration" INT, + "title" VARCHAR(45), + "custom_metadata" BOOLEAN, + PRIMARY KEY ("id") +); + +-- ---------------------------------------------------- +-- Relation between YouTube and Metadata -- +-- ---------------------------------------------------- +CREATE TABLE IF NOT EXISTS youtubemd.Video_Has_Metadata +( + "id" VARCHAR(11) NOT NULL, + "metadata_id" INT NOT NULL, + PRIMARY KEY ("id", "metadata_id"), + CONSTRAINT "fk_video_id" + FOREIGN KEY ("id") + REFERENCES youtubemd.YouTube ("id"), + CONSTRAINT "fk_metadata_id" + FOREIGN KEY ("metadata_id") + REFERENCES youtubemd.Metadata ("id") +); + +-- -------------------------------------- +-- Table File -- +-- -------------------------------------- +CREATE TABLE IF NOT EXISTS youtubemd.File +( + "id" VARCHAR(50) UNIQUE NOT NULL, + "metadata_id" INT UNIQUE NOT NULL, + "audio_quality" AQUALITY NOT NULL, + "size" INT, + PRIMARY KEY ("id", "metadata_id"), + CONSTRAINT "fk_metadata_id" + FOREIGN KEY ("metadata_id") + REFERENCES youtubemd.Metadata ("id") +); + +-- ----------------------------------------- +-- Table History -- +-- ----------------------------------------- +CREATE TABLE IF NOT EXISTS youtubemd.History +( + "id" BIGSERIAL NOT NULL, + "file_id" VARCHAR(50) NOT NULL, + "user_id" INT NOT NULL, + "metadata_id" INT NOT NULL, + "date" date, + PRIMARY KEY ("id", "file_id", "user_id", "metadata_id"), + CONSTRAINT "fk_user_id" + FOREIGN KEY ("user_id") + REFERENCES youtubemd.User ("id") + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT "fk_file_id" + FOREIGN KEY ("file_id") + REFERENCES youtubemd.File ("id"), + CONSTRAINT "fk_metadata_id" + FOREIGN KEY ("metadata_id") + REFERENCES youtubemd.Metadata ("id") +); + +-- ------------------------------------------ +-- Table Playlist -- +-- ------------------------------------------ +CREATE TABLE IF NOT EXISTS youtubemd.Playlist +( + "id" VARCHAR(22) NOT NULL UNIQUE, + PRIMARY KEY ("id") +); + +-- ---------------------------------------------- +-- Table YouTube stats -- +-- ---------------------------------------------- +CREATE TABLE IF NOT EXISTS youtubemd.YouTubeStats +( + "id" VARCHAR(11) NOT NULL UNIQUE, + "daily_requests" INT NOT NULL DEFAULT 1, + "weekly_requests" INT NOT NULL DEFAULT 1, + "monthly_requests" INT NOT NULL DEFAULT 1, + PRIMARY KEY ("id"), + CONSTRAINT "fk_youtube_id" + FOREIGN KEY ("id") + REFERENCES youtubemd.YouTube ("id") +); + +-- Additional indexes +CREATE INDEX youtubemd.user_preferences_ix ON youtubemd.Preferences ("user_id"); +CREATE INDEX youtubemd.video_metadata_ix ON youtubemd.Video_Has_Metadata ("id", "metadata_id"); +CREATE INDEX youtubemd.history_ix ON youtubemd.History ("id", "file_id", "user_id", "metadata_id"); + +-- Trigger that updated the different stats +CREATE FUNCTION youtubemd.process_stats() RETURNS trigger AS +$$ +DECLARE + daily_value INT; + weekly_value INT; + monthly_value INT; +BEGIN + IF (SELECT EXISTS(SELECT 1 FROM youtubemd.YouTubeStats WHERE youtubemd.YouTubeStats.id = NEW.id)) THEN + SELECT INTO daily_value, weekly_value, monthly_value youtubemd.YouTubeStats.daily_requests, + youtubemd.YouTubeStats.weekly_requests, + youtubemd.YouTubeStats.monthly_requests + FROM youtubemd.YouTubeStats + WHERE youtubemd.YouTubeStats.id = NEW.id; + daily_value = daily_value + 1; + weekly_value = weekly_value + 1; + monthly_value = monthly_value + 1; + + UPDATE youtubemd.YouTubeStats + SET daily_requests = daily_value, + weekly_requests = weekly_value, + monthly_requests = monthly_value + WHERE id = NEW.id; + ELSE + INSERT INTO youtubemd.YouTubeStats (id) VALUES (NEW.id); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Complementary functions with useful operations +CREATE FUNCTION youtubemd.top_10_daily() + RETURNS TABLE + ( + id VARCHAR(11), + daily_requests INT + ) +AS +$$ +BEGIN + RETURN QUERY SELECT DISTINCT youtubemd.YouTubeStats.id, youtubemd.YouTubeStats.daily_requests + FROM youtubemd.youtubestats + ORDER BY daily_requests DESC + FETCH FIRST 10 ROWS ONLY; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION youtubemd.top_10_weekly() + RETURNS TABLE + ( + id VARCHAR(11), + weekly_requests INT + ) +AS +$$ +BEGIN + RETURN QUERY SELECT DISTINCT youtubemd.YouTubeStats.id, youtubemd.YouTubeStats.weekly_requests + FROM youtubemd.youtubestats + ORDER BY weekly_requests DESC + FETCH FIRST 10 ROWS ONLY; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION youtubemd.top_10_monthly() + RETURNS TABLE + ( + id VARCHAR(11), + monthly_requests INT + ) +AS +$$ +BEGIN + RETURN QUERY SELECT DISTINCT youtubemd.YouTubeStats.id, youtubemd.YouTubeStats.monthly_requests + FROM youtubemd.youtubestats + ORDER BY monthly_requests DESC + FETCH FIRST 10 ROWS ONLY; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION youtubemd.clear_daily_stats() AS +$$ +BEGIN + UPDATE youtubemd.YouTubeStats SET daily_requests = 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION youtubemd.clear_weekly_stats() AS +$$ +BEGIN + UPDATE youtubemd.YouTubeStats SET weekly_requests = 0; +END; +$$ LANGUAGE plpgsql; + +CREATE FUNCTION youtubemd.clear_monthly_stats() AS +$$ +BEGIN + UPDATE youtubemd.YouTubeStats SET monthly_requests = 0; +END; +$$ LANGUAGE plpgsql; + +-- Init the trigger +CREATE TRIGGER stats_update + AFTER INSERT OR UPDATE + ON youtubemd.YouTube + FOR EACH ROW +EXECUTE PROCEDURE youtubemd.process_stats(); diff --git a/YouTubeMDBot/.idea/.gitignore b/YouTubeMDBot/.idea/.gitignore new file mode 100755 index 0000000..0e40fe8 --- /dev/null +++ b/YouTubeMDBot/.idea/.gitignore @@ -0,0 +1,3 @@ + +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/YouTubeMDBot/.idea/YouTubeMDBot.iml b/YouTubeMDBot/.idea/YouTubeMDBot.iml new file mode 100755 index 0000000..6711606 --- /dev/null +++ b/YouTubeMDBot/.idea/YouTubeMDBot.iml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/codeStyles/codeStyleConfig.xml b/YouTubeMDBot/.idea/codeStyles/codeStyleConfig.xml new file mode 100755 index 0000000..a55e7a1 --- /dev/null +++ b/YouTubeMDBot/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/dictionaries/javinator9889.xml b/YouTubeMDBot/.idea/dictionaries/javinator9889.xml new file mode 100755 index 0000000..787b6ce --- /dev/null +++ b/YouTubeMDBot/.idea/dictionaries/javinator9889.xml @@ -0,0 +1,10 @@ + + + + acoustid + ffmpeg + fpcalc + javinator + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/git_toolbox_prj.xml b/YouTubeMDBot/.idea/git_toolbox_prj.xml new file mode 100644 index 0000000..c7846c0 --- /dev/null +++ b/YouTubeMDBot/.idea/git_toolbox_prj.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/inspectionProfiles/profiles_settings.xml b/YouTubeMDBot/.idea/inspectionProfiles/profiles_settings.xml new file mode 100755 index 0000000..105ce2d --- /dev/null +++ b/YouTubeMDBot/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/misc.xml b/YouTubeMDBot/.idea/misc.xml new file mode 100755 index 0000000..8656114 --- /dev/null +++ b/YouTubeMDBot/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/modules.xml b/YouTubeMDBot/.idea/modules.xml new file mode 100755 index 0000000..48cc268 --- /dev/null +++ b/YouTubeMDBot/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/YouTubeMDBot/.idea/vcs.xml b/YouTubeMDBot/.idea/vcs.xml new file mode 100755 index 0000000..6c0b863 --- /dev/null +++ b/YouTubeMDBot/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file