# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
__license__   = 'GPL v3'
__copyright__ = '2014, DaltonST <DaltonShiTzu@outlook.com>'
__docformat__ = 'restructuredtext en'
__my_version__ = "3.6.26"
import os, sys
import apsw
from calibre.library.database import LibraryDatabase
import datetime, re, sre_constants
from calibre.db.backend import DB
from calibre import isbytestring, force_unicode, prints
from calibre.constants import filesystem_encoding, preferred_encoding
from Queue import Empty, Queue
from calibre.gui2.threaded_jobs import ThreadedJob, BaseJob
from calibre.utils.logging import Log as log
from calibre.gui2 import error_dialog, info_dialog
import codecs
from time import sleep
import time
import unicodedata
from copy import deepcopy
from difflib import SequenceMatcher
from calibre_plugins.quarantine_and_scrub.heading import log_heading_common
header_s1 = None
header_s2 = None
header_s3 = None
header_s4 = None
header_s5 = None
notifications = Queue()
mynothing = ""
scrubbed_books_final_list = []
candidate_list = []
my_series_priority = "Pristine,Global,Work"
my_run_type = "0"
n_cutoff = 0.700000
n_cutoff_minor = 0.750000
def main_scrub_series_level(self, guidb, series_source_priority, run_type, log=None, abort=None, notifications=True):
    global header_s1
    global header_s2
    global header_s3
    global header_s4
    global header_s5
    global mynothing
    global my_series_priority
    global my_run_type
    global n_cutoff
    my_run_type = run_type
    if my_run_type <> "0" and my_run_type <> "1":
        my_run_type = "0"
        header_s4 = "Error: invalid type of run"
        return
    if  my_run_type == "0":
        header_s4 = "......What-if only......no Actual updates will be made."
    else:
        header_s4 = "Actual updates to be performed"
    header_s5 = str("Series Source Priority is: " + str(series_source_priority))
    my_series_priority = str(series_source_priority)
    if (not my_series_priority) or (my_series_priority == mynothing):
        my_series_priority = str("Pristine,Global,Work")
    notifications.put((0.01, 'Beginning Series-Level Scrubbing'))
    db = guidb
    path = db.library_path
    if isbytestring(path):
        path = path.decode(filesystem_encoding)
    path = path.replace(os.sep, '/')
    path = os.path.join(path, 'metadata.db')
    path = path.replace(os.sep, '/')
    log("Library DB: " + path)
    try:
        my_db =apsw.Connection(path)
    except Exception as e:
        log(str(e))
        raise e
        return
    my_cursor = my_db.cursor()
    header_s1 =  str("SQLite Version: " + str(apsw.SQLITE_VERSION_NUMBER) + "  [APSW]") 
    mysql = str("PRAGMA main.locking_mode=EXCLUSIVE;")
    my_cursor.execute(mysql)
    header_s2 = mysql
    sleep(0.5)
    Scrub_Control(my_db, my_cursor, notifications, log)
    my_db.close()
    log("Job complete.")
    return
def Scrub_Control(my_db, my_cursor, notifications, log):
    global header_s1
    global header_s2
    global header_s3
    global header_s4
    global header_s5
    header_s3 = "Beginning Series Level Scrubbing"
    log_heading_common(log,header_s1,header_s2,header_s3,header_s4,header_s5)
    mysql = "PRAGMA main.busy_timeout = 5000;"    
    my_cursor.execute(mysql)
    mysql = "SELECT Count(*) FROM custom_column_10"
    my_cursor.execute(mysql)
    count1 = my_cursor.fetchall()
    count1 = __strip_numerics(count1)
    log("Number of Work Series:    ", str(count1))
    if str(count1) == "[0]" or str(count1) == "0":
        log("Nothing to do.")
        log("Series-Level Consolidation/Renaming is complete.")
        notifications.put((1.0, 'Series Level Scrubbing Complete'))
        return
    notifications.put((0.01, 'Adding New Global Series from WSSVD'))
    __add_new_global_series_from_wssvd(my_db, my_cursor, log)
    notifications.put((0.02, 'Counting Global/Pristine Series'))
    __count_global_series(my_db, my_cursor, log)
    notifications.put((0.03, 'Doing Light Work Series Housekeeping'))
    __explode_custom_column_10_if_needed(my_db, my_cursor, log)
    __refresh_custom_column_15(my_db, my_cursor, notifications, log)
    notifications.put((0.04, 'Comparing Each Work Series to Other Work Series, Global Series & Pristine Series'))
    log("Comparing Each Work Series to Other Work Series, Global Series & Pristine Series")
    build_instr_series_series_table(my_db, my_cursor, notifications, log)
    notifications.put((0.50, 'Selecting Appropriate Series Names'))
    log("Selecting Appropriate Series Names")
    process_instr_series_series_table(my_db, my_cursor, notifications, log)
    n = len(candidate_list)
    if n == 0 :
        notifications.put((0.99, 'Nothing To Rename'))
        log("Nothing To Rename")
    else:
        notifications.put((0.95, 'Renaming Series Names'))
        log("Consolidating Series by Renaming Series Names")
        update_results(my_db, my_cursor, notifications, log)
        __explode_custom_column_10_if_needed(my_db, my_cursor, log)
        __refresh_custom_column_15(my_db, my_cursor, notifications, log)
    __delete_unused_values(my_db, my_cursor, notifications, log)
    log("Series-Level Consolidation/Renaming is complete.")
    notifications.put((1.0, 'Series Level Scrubbing Complete'))
def  update_results(my_db, my_cursor, notifications, log):
    global candidate_list
    global my_run_type
    n = len(candidate_list)
    if n == 0 :
        return
    else:
        pass
    final_list = []
    for row in candidate_list:
        if row not in final_list:
            final_list.append(row)
        else:
            pass
    candidate_dict = {}
    n_changes = 0
    log(" ")
    for row in final_list:
        candidate_dict = row
        for k,v in candidate_dict.iteritems():
            s_old_series = str(k)
            s_new_series = str(v)
            s = str("Work Series: [ " + str(s_old_series) + " ] will be changed to: [ " + str(s_new_series) + " ]")
            log(str(s))
            if my_run_type == "0":
                pass
            else:
                mysql = str("UPDATE custom_column_10 SET value = ? WHERE value = ? ")
                __execute_mysql_for_custom_column_generic(my_db, my_cursor, log, mysql, str(s_new_series), str(s_old_series))
                n_changes = n_changes + 1
        log(" ")
    log(" ")
    if my_run_type == "0":
        log("No Changes Made; What-if Only")
    else:
        s = str(str(n_changes) + " Actual Changes to Work Series Were Made As Listed Above.")
        log(str(s))
    log(" ")
def process_instr_series_series_table(my_db, my_cursor, notifications, log):
    global candidate_list
    global n_cutoff
    global n_cutoff_minor
    candidate_list[:] = []
    candidate_dict = {}
    log("------------------------------------------------------------------------------------------")
    log("------------------------------------------------------------------------------------------")
    log("Initial Cutoff Similarity Percentage:", str(n_cutoff))
    log("Minor Differences Cutoff Similarity Percentage:", str(n_cutoff_minor))
    log(" ")
    mysql = "SELECT series, name1, name2, sourceseries, sourcename1, sourcename2 FROM _instr_series_series"
    tmp_rows = __execute_mysql_fetchall_generic(my_db, my_cursor, log, mysql)
    tmp_rows.sort()
    s_old_s = ""
    for row in tmp_rows:
        ignore_other_1 = True
        ignore_other_2 = True
        n_count0  = 0
        n_count1  = 0
        n_count2  = 0
        f_ratio1 = 0.000001
        f_ratio1 = 0.000001
        s = ""
        s_special_msg = ""
        swap_in_reverse_1 = False
        swap_in_reverse_2 = False
        log("------------------------------------------------------------------------------------------")
        log("Work Series, Other 1, Other 2, Source, Source 1, Source 2 ")
        log("------------------------------------------------------------------------------------------")
        series,name1,name2,sourceseries,sourcename1,sourcename2 = row
        if not name1:
            name1 = "(nothing)"
        if not name2:
            name1 = "(nothing)"
        s = str(str(series) + " , " + str(name1) + " , "  + str(name2) + " , "  + str(sourceseries) + " , "  + str(sourcename1) + " , "  + str(sourcename2))
        if str(s) == str(s_old_s):
            continue
        s_old_s = str(s)
        log(str(s))
        log(" ")
        n_count0 = count_series_usage(my_db, my_cursor, str(series))
        s = str("Usage of Work Series as Work Series:      " + str(n_count0) + " time(s) ")
        log(s)
        n_count1 = count_series_usage(my_db, my_cursor, str(name1))
        s = str("Usage of Other Series 1 as Work Series:   " + str(n_count1) + " time(s) ")
        log(s)
        n_count2 = count_series_usage(my_db, my_cursor, str(name2))
        s = str("Usage of Other Series 2 as Work Series:   " + str(n_count2) + " time(s) ")
        log(s)
        log(" ")
        f_ratio1 =     __similar(str(series), str(name1))
        s = str("Similarity of Work Series to Other Series 1 is: " + str(f_ratio1))
        log(str(s))
        f_ratio2 =     __similar(str(series), str(name2))
        s = str("Similarity of Work Series to Other Series 2 is: " + str(f_ratio2))
        log(str(s))
        log(" ")
        s_action = str("To Be Determined")
        ignore_other_1 = True
        ignore_other_2 = True
        picked1 = False
        picked2 = False
        picked0_1 = False
        picked0_2 = False
        if f_ratio1 < 1.0 and f_ratio1 >= n_cutoff :
            ignore_other_1 = False
        if f_ratio2 < 1.0 and f_ratio2 >= n_cutoff:
            ignore_other_2 = False 
        if not ignore_other_1:
            if n_count1 > n_count0:
                pass
            else:
                if n_count1 == n_count0:
                    if f_ratio1 >= n_cutoff_minor: 
                        pass
                    else:
                        ignore_other_1 = True
                else:
                    ignore_other_1 = True
        if not ignore_other_2:
            if n_count2 > n_count0:
                pass
            else:
                if n_count2 == n_count0:
                    if f_ratio2 >= n_cutoff_minor: 
                        pass
                    else:
                        ignore_other_2 = True
                else:
                    ignore_other_2 = True
        if f_ratio1 == 1.0:
            ignore_other_1 = True
        if f_ratio2 == 1.0:
            ignore_other_2 = True
        if ignore_other_1 and ignore_other_2:
            s_action = str("None")
        else:
            if (not ignore_other_1) and (not ignore_other_2) :
                if n_count1 == n_count2 :
                    if f_ratio1 > f_ratio2:
                        ignore_other_2 = True
                    else:
                        ignore_other_1 = True
            else:
                if not ignore_other_1:
                    if n_count1 > n_count0 :
                        picked1 = True
                    else:
                        if n_count1 == n_count0:
                            if f_ratio1 >= n_cutoff_minor: 
                                series = str(__change_tuple_to_bytestring(series))
                                name1 = str(__change_tuple_to_bytestring(name1))
                                if (((not series.startswith("The ")) and name1.startswith("The ")) or ((not series.startswith("A ")) and name1.startswith("A "))
                                                                                                                                      or ((not series.startswith("An ")) and name1.startswith("An "))):
                                    picked1 = True
                                    s_special_msg = "Other 1 selected due to minor differences (The/A/An) cutoff percentage although counts were identical."
                                else:
                                    if (((series.startswith("The ")) and (not name1.startswith("The "))) or ((series.startswith("A ")) and (not name1.startswith("A ")))
                                                                                                                                            or ((series.startswith("An ")) and (not name1.startswith("An ")))):
                                        picked1 = False
                                        swap_in_reverse_1 = True
                                        picked0_1 = True
                                        s_special_msg = "Reverse Swap: Series Will Replace Other 1 due to (The/A/An) cutoff percentage although counts were identical."
                                    else:
                                        s_action = str("None")
                        else:
                            s_action = str("None")
                else:
                    if not ignore_other_2:
                        if n_count2 > n_count0 :
                            picked2 = True
                        else:
                            if n_count2 == n_count0:
                                if f_ratio2 >= n_cutoff_minor: 
                                    series = str(__change_tuple_to_bytestring(series))
                                    name2 = str(__change_tuple_to_bytestring(name2))
                                    if (((not series.startswith("The ")) and name2.startswith("The ")) or ((not series.startswith("A ")) and name2.startswith("A "))
                                                                                                                                          or ((not series.startswith("An ")) and name2.startswith("An "))):
                                        picked2 = True
                                        s_special_msg = "Other 2 selected due to minor differences (The/A/An) cutoff percentage although counts were identical."
                                    else:
                                        if (((series.startswith("The ")) and (not name2.startswith("The "))) or ((series.startswith("A ")) and (not name2.startswith("A ")))
                                                                                                                                                or ((series.startswith("An ")) and (not name2.startswith("An ")))):
                                            picked2 = False
                                            swap_in_reverse_2 = True
                                            picked0_2 = True
                                            s_special_msg = "Reverse Swap: Series Will Replace Other 2 due to (The/A/An) cutoff percentage although counts were identical."
                                        else:
                                            s_action = str("None")
                            else:
                                s_action = str("None")
                    else:
                        s_action = str("None")
        if swap_in_reverse_1 :
            if sourcename1 <> "series":
                swap_in_reverse_1 = False
                picked0_1 = False
                s_special_msg = ""
            else:
                pass
        if swap_in_reverse_2 :
            if sourcename2 <> "series":
                swap_in_reverse_2 = False
                picked0_2 = False
                s_special_msg = ""
            else:
                pass
        if (picked1 and picked2) or (picked1 and picked0_1) or (picked2 and picked0_2):
            log("ERROR: Too Many Choices Chosen for:" + str(series))
            continue
        else:
            del candidate_dict
            candidate_dict = {}
            if picked1:
                s_action = str("Other Series 1: [ " + str(name1) + " ] Will Replace: [ " + str(series) + " ]")
                candidate_dict[str(series)] = str(name1)
                candidate_list.append(candidate_dict)
            else:
                if picked2:
                    s_action = str("Other Series 2: [ " + str(name2) + " ] Will Replace: [ " + str(series) + " ]")
                    candidate_dict[str(series)] = str(name2)
                    candidate_list.append(candidate_dict)
                else:
                    if swap_in_reverse_1 :#change name1 to series, not the other way around
                        s_action = str("Work Series : [ " + str(series) + " ] Will Replace Other Series 1: [ " + str(name1) + " ]")
                        candidate_dict[str(name1)] = str(series)
                        candidate_list.append(candidate_dict)
                    else:
                        if swap_in_reverse_2 :
                            s_action = str("Work Series : [ " + str(series) + " ] Will Replace Other Series 2: [ " + str(name2) + " ]")
                            candidate_dict[str(name2)] = str(series)
                            candidate_list.append(candidate_dict)
                        else:
                            s_action = str("None")
        if s_special_msg <> "":
            log(" ")
            log(s_special_msg)
            log(" ")
        log ("Action To Be Taken:", str(s_action))
        log(" ")
    log("----------------------------------------------------------------------------------------")
    log("----------------------------------------------------------------------------------------")
    log("----------------------------------------------------------------------------------------")
def build_instr_series_series_table(my_db, my_cursor, notifications, log):
    global my_series_priority
    series_source_priority_1 = "Pristine,Global,Work"
    series_source_priority_2 = "Global,Work"
    series_source_priority_3 = "Work"
    mysql = "DELETE FROM _instr_series_series WHERE id > '0'"
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    sleep(0.5)
    if my_series_priority == series_source_priority_1:
        notifications.put((0.05, 'Sources:  Pristine,Global,Work -  Comparing Each Work Series to Other Work Series, Global Series & Pristine Series'))
        log("Highest Priority For Similarity Matching is: Pristine Series")
        mysql = "INSERT OR IGNORE INTO _instr_series_series SELECT null,seriesname,name1,name2, 'series','series','pristine' FROM  __instr_series_within_pristine_series_part2"
        __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
        sleep(1.0)
        notifications.put((0.10, 'Sources:  Pristine,Global,Work -  Comparing Each Work Series to Other Work Series, Global Series & Pristine Series'))
        log("Middle Priority For Similarity Matching is: Global Series")
        mysql = "INSERT OR IGNORE INTO _instr_series_series SELECT null,seriesname,name1,name2, 'series','series','global' FROM  __instr_series_within_global_series_part2"
        __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
        sleep(1.0)
        notifications.put((0.40, 'Sources:  Pristine,Global,Work -  Comparing Each Work Series to Other Work Series, Global Series & Pristine Series'))
        log("Lowest Priority For Similarity Matching is: Other Current Work Series")
        mysql = "INSERT OR IGNORE INTO _instr_series_series SELECT null,seriesname,name1,name2, 'series','series','series'  FROM  __instr_series_within_series_part2"
        __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
        sleep(1.0)
        return
    if my_series_priority == series_source_priority_2:
        sleep(1.0)
        notifications.put((0.05, 'Sources:  Global,Work -   Comparing Each Work Series to Other Work Series & Global Series'))
        log("Higher Priority For Similarity Matching is: Global Series")
        mysql = "INSERT OR IGNORE INTO _instr_series_series SELECT null,seriesname,name1,name2, 'series','series','global' FROM  __instr_series_within_global_series_part2"
        __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
        sleep(1.0)
        notifications.put((0.40, 'Sources:  Global,Work -   Comparing Each Work Series to Other Work Series & Global Series'))
        log("Lower Priority For Similarity Matching is: Other Current Work Series")
        mysql = "INSERT OR IGNORE INTO _instr_series_series SELECT null,seriesname,name1,name2, 'series','series','series'  FROM  __instr_series_within_series_part2"
        __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
        sleep(1.0)
        return
    if my_series_priority == series_source_priority_3:
        sleep(1.0)
        notifications.put((0.05, 'Sources:  Work -  Comparing Each Work Series to Other Work Series'))
        log("Single Source for Similarity Matching is: Other Current Work Series")
        mysql = "INSERT OR IGNORE INTO _instr_series_series SELECT null,seriesname,name1,name2, 'series','series','series'  FROM  __instr_series_within_series_part2"
        __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
        sleep(1.0)
        return
    log("Error.  Series Source Priority Invalid.  Processing Terminated Early.")
    return
def count_series_usage(my_db, my_cursor, s_series):
    if not s_series:
        return 0
    if s_series == "":
        return 0
    s_series = str(s_series)
    mysql = 'SELECT Count(*) FROM __series_work_full WHERE seriesname = "' + s_series + '" ;'
    my_cursor.execute(mysql)
    tmprows = my_cursor.fetchall()
    if not tmprows:
        return 0
    else:
        for row in tmprows:
            s_count = str(__strip_numerics(row))
            n_count = int(float(s_count))
            break
    return n_count
def __refresh_custom_column_15(my_db, my_cursor, notifications, log):
    c_work_series_full_table = "custom_column_15"
    mysql = "DELETE FROM custom_column_15 WHERE book  > 0 "
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    c_work_series_full_table = "custom_column_15" 
    mysql = "INSERT or IGNORE INTO custom_column_15 SELECT book,book,seriesfull FROM __series_work_full WHERE book > 0 "
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    c_work_series_full_table = "custom_column_15" 
    mysql =  "UPDATE custom_column_15 SET value=REPLACE(value,'.0','') WHERE book > 0 "
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    c_work_series_full_table = "custom_column_15" 
    mysql =  "UPDATE custom_column_15 SET value=REPLACE(value,'.5]','.50]') WHERE book > 0 "
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
def __explode_custom_column_10_if_needed(my_db, my_cursor, log):
    sleep(0.1)
    mysql = 'DELETE FROM custom_column_10 WHERE id IN __series_unused'
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    mysql = "INSERT or REPLACE INTO custom_column_10 (id,value) SELECT book, seriesname FROM __books_work_populate WHERE seriesname > ' '  ; "
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    sleep(0.1)
    mysql = "UPDATE books_custom_column_10_link  SET value = books_custom_column_10_link.book "
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    sleep(0.1)
    mysql = "DELETE FROM custom_column_10 WHERE id NOT IN (SELECT value FROM books_custom_column_10_link)"
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    sleep(0.1)
    mysql = 'DELETE FROM custom_column_10 WHERE id IN __series_unused'
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
def __similar(a,b):
    return SequenceMatcher(None, a, b).ratio()
def __printblanks(n):
    n1 = int(n)
    if n1 <1:
        n1 = 1
    for i in range (1, n1):
        pass
def __printsafe(*arg):
    try:
        pass
    except:
        pass
def __delete_unused_values(my_db, my_cursor, notifications, log):
    mysql = 'DELETE FROM custom_column_10 WHERE id IN __series_unused'
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
def __count_global_series(my_db, my_cursor, log):
    log("===================================")
    log("For Your Reference")
    log("===================================")
    mysql = "SELECT Count(*) FROM _global_series"
    my_cursor.execute(mysql)
    count2 = my_cursor.fetchall()
    count2 = __strip_numerics(count2)
    log("Number of Global Series:    ", str(count2))
    mysql = "SELECT Count(*) FROM _pristine_series"
    my_cursor.execute(mysql)
    count4 = my_cursor.fetchall()
    count4 = __strip_numerics(count4)
    log("Number of Pristine Series:  ", str(count4))
    log("===================================")
    log("===================================")
def __execute_mysql_fetchall_generic(my_db, my_cursor, log, mysql):
    sleep(0.1) #avoid db locks
    tmp_rows = []
    del tmp_rows
    tmp_rows = []
    mysql = str(mysql)
    try:
        my_cursor.execute(mysql)
        tmp_rows = my_cursor.fetchall() #will get all of the rows from the query
        if tmp_rows:
            return tmp_rows
        else:
            tmp_rows = []
            return tmp_rows
    except:
        sleep(11.0)
        tmp_rows = []
        del tmp_rows
        tmp_rows = []
        try:
            my_cursor.execute(mysql)
            tmp_rows = my_cursor.fetchall() #will get all of the rows from the query
            if tmp_rows:
                return tmp_rows
            else:
                tmp_rows = []
                return tmp_rows
        except Exception as e:
            log(str(e))
            my_db.close()
            raise e
            sys.exit("ERROR in __execute_mysql_fetchall_generic....")
def __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql):
    global freeze_current_book
    freeze_current_book = True
    mysql = str(mysql)
    try:
        my_cursor.execute("begin") #apsw
        my_cursor.execute(mysql)
        my_cursor.execute("commit") #apsw
    except:
        sleep(5.0)
        try:
            my_cursor.execute(mysql)
            my_cursor.execute("commit") #apsw
        except Exception as e:
            log(str(e))
            my_db.close()
            raise e
            sys.exit("ERROR in __execute_mysql_with_commit_generic....")
    sleep(0.03) #avoid db locks
def __execute_mysql_for_custom_column_generic(my_db, my_cursor, log, mysql, v1, v2):
    global mynothing
    global freeze_current_book
    freeze_current_book = True
    s2 = str(v2)
    s2 = str(__change_tuple_to_bytestring(s2))
    if s2 == mynothing:
        s2 = str("IGNORE")
    try:
        my_cursor.execute("begin") #apsw
        if s2 <> "IGNORE":
            my_cursor.execute(mysql,(v1,v2))
        else:
            my_cursor.execute(mysql,(v1))
        my_cursor.execute("commit") #apsw
    except:
        log("database locked; sleeping and then will retry")
        sleep(25.0)
        try:
            if s2 <> "IGNORE":
                my_cursor.execute(mysql,(v1,v2))
            else:
                my_cursor.execute(mysql,(v1))
            my_cursor.execute("commit") #apsw
        except Exception as e:
            log(str(e))
            my_db.close()
            raise e
            sys.exit("ERROR in __execute_mysql_for_custom_column_generic")
    sleep(0.03) #avoid db locks
def __execute_mysql_for_custom_column_generic_3_args(my_db, my_cursor, log, mysql, v1, v2, v3):
    global mynothing
    global freeze_current_book
    freeze_current_book = True
    try:
        my_cursor.execute("begin") #apsw
        my_cursor.execute(mysql,(v1,v2,v3))
        my_cursor.execute("commit") #apsw
    except Exception as e:
        log(str(e))
        my_db.close()
        raise e
        sys.exit("ERROR in __execute_mysql_for_custom_column_generic_3_args")
    sleep(0.03) #avoid db locks
def __strip_numerics(s):
    global mynothing
    s = str(s)
    s = __change_tuple_to_bytestring(s)
    s = s.replace(',', mynothing, 4)
    s = s.replace("(", mynothing, 4)
    s = s.replace(")", mynothing, 4)
    s = s.replace(" ", mynothing, 10)
    s = s.strip()
    return s
def __change_tuple_to_bytestring(tuple):
    global mynothing
    s_string = str(tuple)
    s_string = s_string.replace('(u"', mynothing, 100)
    s_string = s_string.replace("(u'", mynothing, 100)
    s_string = s_string.replace("',))", mynothing, 100)
    s_string = s_string.replace("',)", mynothing, 100)
    s_string = s_string.replace('",)', mynothing, 100)
    s_string = s_string.replace('u"', mynothing, 100) 
    s_string = s_string.replace("u'", mynothing, 100)
    s_string = s_string.replace("'(", "'", 100)
    s_string = s_string.replace(")'", "'", 100)
    s_string = s_string.replace('u"', mynothing, 100)
    s_string = s_string.replace('")', mynothing, 100)
    s_string = str(s_string)
    n1 = s_string.find("(")
    n2 = s_string.find(")")
    if n1 >= 0:
        if n2 >= 0 and n2 > n1: 
            return s_string
        else:
            pass
    n1 = s_string.count("(")
    n2 = s_string.count(")")
    if n2 == 2 and n1 == 1: 
        return s_string
    if n2 == 1 and n1 == 0: 
        return s_string
    n = s_string.find("(")
    if n == 0: #example:  is (199)
        s_string = s_string.replace("(", mynothing, 1)
        c = s_string.count(")")
        if c == 1:
            s_string = s_string.replace(")", mynothing, 1)
        else:
            pass
    n = s_string.rfind("'")
    l = len(s_string)
    if n == (l - 1):
        z = (n - 1)
        s_string = s_string[0:z]
    else:
        pass
    n = s_string.rfind(")")
    l = len(s_string)
    if n == (l - 1):
        z = (n - 1)
        s_string = s_string[0:z]
        pass
    n = s_string.rfind("'")
    l = len(s_string)
    if n == (l - 1):
        z = (n - 1)
        s_string = s_string[0:z]
    else:
        pass
    n = s_string.rfind(")")
    l = len(s_string)
    if n == (l - 1):
        z = (n - 1)
        s_string = s_string[0:z]
        pass
    s_string = s_string.strip()
    if isinstance(s_string, unicode):
        s_string = str(s_string)
        if isinstance(s_string, unicode):
            pass
    return s_string
def  __add_change_delete_instr_series_views(my_db, my_cursor, log):
    return
def __add_new_global_series_from_wssvd(my_db, my_cursor, log):
    mysql = "INSERT OR IGNORE INTO _global_authors SELECT null,authname,' ',' ' FROM _global_web_author_series"
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    mysql = "INSERT OR IGNORE INTO _global_series SELECT null,seriesname,' ' FROM _global_web_author_series"
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    mysql = "INSERT OR IGNORE INTO _global_author_series_link SELECT null,authid,seriesid FROM __global_author_series_link_in_wssvd WHERE authid NOT NULL and seriesid NOT NULL"
    __execute_mysql_with_commit_generic(my_db, my_cursor, log, mysql)
    log("If Available, New Global Series and their Global Authors Added from Web Source Series Validation Data (WSSVD)")
