HEX
Server: Apache/2.4.63 (Unix)
System: Linux Synopilou92 4.4.302+ #72806 SMP Mon Jul 21 23:16:00 CST 2025 x86_64
User: pilou92 (1026)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: /volume1/@appstore/SynologyPhotos/migration/python/dump_moments_user_person.py
#!/usr/bin/env python3

import json
import logging
import sys
import os
import pg8000

from argparse import ArgumentParser


def init_cursor():
    user = "postgres"
    unix_sock = "/var/run/postgresql/.s.PGSQL.5432"
    database = "synophoto"
    conn = pg8000.connect(user, unix_sock=unix_sock, database=database)

    cursor = conn.cursor()
    cursor.execute("SET CLIENT_ENCODING TO 'UTF8'")

    return cursor


def GetAllUserInfo(cursor):
    cursor.execute(
        """
        SELECT id, uid, name, config, enable FROM user_info
    """
    )
    result = toJSON(cursor)
    return result


def GetUserInfoById(cursor, id):
    cursor.execute(
        """
        SELECT id, uid, name, config, enable FROM user_info
        WHERE id = {}
    """.format(
            id
        )
    )
    result = toJSON(cursor)
    return result


def GetNamedAndHiddenPerson(cursor, id_user_info, offset, limit):
    cursor.execute(
        """
        SELECT * FROM user_{}.person
        WHERE name <> '' OR hidden = 't'
        ORDER BY id
        OFFSET {} LIMIT {}
    """.format(
            id_user_info, offset, limit
        )
    )
    result = toJSON(cursor)
    return result


def GetFace(cursor, id_user_info, id_person, offset, limit):
    cursor.execute(
        """
        SELECT id, bounding_box, id_unit, ref_id_unit
        FROM user_{}.face
        WHERE id_person = {}
        ORDER BY id
        OFFSET {} LIMIT {}
    """.format(
            id_user_info, id_person, offset, limit
        )
    )

    result = []
    for id, bounding_box, id_unit, ref_id_unit in cursor.fetchall():
        result.append(
            {
                "id": id,
                "bounding_box": bounding_box,
                "path": GetPath(cursor, id_user_info, ref_id_unit),
                "id_unit": id_unit,
                "ref_id_unit": ref_id_unit,
            }
        )
    return result


def GetPath(cursor, id_user_info, ref_id_unit):
    cursor.execute(
        """
        SELECT filename, id_folder FROM user_{}.unit
        WHERE id = {}
    """.format(
            id_user_info, ref_id_unit
        )
    )
    filename, id_folder = cursor.fetchone()

    cursor.execute(
        """
        SELECT name FROM user_{}.folder
        WHERE id = {}
    """.format(
            id_user_info, id_folder
        )
    )
    [folder_name] = cursor.fetchone()

    return os.path.join(folder_name, filename)


def CountFaceNeedToMigrate(cursor, id_user_info):
    cursor.execute(
        """
        SELECT count(*) FROM user_{}.face
        WHERE id_person IN (
            SELECT id FROM user_{}.person
            WHERE name <> '' OR hidden = 't'
        )
    """.format(
            id_user_info, id_user_info
        )
    )
    count = toJSON(cursor)

    return count


def GetTeamLibraryPath(cursor):
    cursor.execute(
        """
        SELECT path FROM team_library
        ORDER BY name ASC
    """
    )
    path = ""

    if row := cursor.fetchone():
        [path] = row

    return {"path": path}


def DumpAllFace(unit_cursor, id_user_info):
    unit_cursor.execute(
        """
        SELECT id, filename, id_folder FROM user_{}.unit
        ORDER BY id_folder, id
    """.format(
            id_user_info
        )
    )

    data_cursor = init_cursor()
    result = {}

    while True:
        row = unit_cursor.fetchone()
        if row == None:
            break
        id_unit, filename, id_folder = row

        data_cursor.execute(
            """
            SELECT name FROM user_{}.folder
            WHERE id = {}
        """.format(
                id_user_info, id_folder
            )
        )
        [folder_name] = data_cursor.fetchone()

        path = os.path.join(folder_name, filename)

        faces = []

        data_cursor.execute(
            """
            SELECT bounding_box FROM user_{}.face
            WHERE ref_id_unit = {}
        """.format(
                id_user_info, id_unit
            )
        )

        for [bounding_box] in data_cursor.fetchall():
            faces.append(bounding_box)

        result[path] = faces

    return result


def toJSON(cursor):
    cols = [desc[0].decode("utf-8") for desc in cursor.description]
    result = [dict(zip(cols, row)) for row in cursor]
    return result


def args_parser(argv):
    argparser = ArgumentParser()
    argparser.add_argument("output_path", type=str, help="specify output filepath")
    subparsers = argparser.add_subparsers()

    GetAllUserInfo_parser = subparsers.add_parser("GetAllUserInfo")
    GetAllUserInfo_parser.set_defaults(function=GetAllUserInfo, which="GetAllUserInfo")

    GetUserInfoById_parser = subparsers.add_parser("GetUserInfoById")
    GetUserInfoById_parser.add_argument(
        "--id-user", type=int, required=True, help="user id"
    )
    GetUserInfoById_parser.set_defaults(
        function=GetUserInfoById, which="GetUserInfoById"
    )

    GetNamedAndHiddenPerson_parser = subparsers.add_parser("GetNamedAndHiddenPerson")
    GetNamedAndHiddenPerson_parser.add_argument(
        "--id-user", type=int, required=True, help="user id"
    )
    GetNamedAndHiddenPerson_parser.add_argument(
        "-o", "--offset", type=int, required=True, help="offset"
    )
    GetNamedAndHiddenPerson_parser.add_argument(
        "-l", "--limit", type=int, required=True, help="limit"
    )
    GetNamedAndHiddenPerson_parser.set_defaults(
        function=GetNamedAndHiddenPerson, which="GetNamedAndHiddenPerson"
    )

    GetFace_parser = subparsers.add_parser("GetFace")
    GetFace_parser.add_argument("--id-user", type=int, required=True, help="user id")
    GetFace_parser.add_argument(
        "--id-person", type=int, required=True, help="person id"
    )
    GetFace_parser.add_argument(
        "-o", "--offset", type=int, required=True, help="offset"
    )
    GetFace_parser.add_argument("-l", "--limit", type=int, required=True, help="limit")
    GetFace_parser.set_defaults(function=GetFace, which="GetFace")

    CountFaceNeedToMigrate_parser = subparsers.add_parser("CountFaceNeedToMigrate")
    CountFaceNeedToMigrate_parser.add_argument(
        "--id-user", type=int, required=True, help="user id"
    )
    CountFaceNeedToMigrate_parser.set_defaults(
        function=CountFaceNeedToMigrate, which="CountFaceNeedToMigrate"
    )

    GetTeamLibraryPath_parser = subparsers.add_parser("GetTeamLibraryPath")
    GetTeamLibraryPath_parser.set_defaults(
        function=GetTeamLibraryPath, which="GetTeamLibraryPath"
    )

    DumpAllFace_parser = subparsers.add_parser("DumpAllFace")
    DumpAllFace_parser.add_argument(
        "--id-user", type=int, required=True, help="user id"
    )
    DumpAllFace_parser.set_defaults(function=DumpAllFace, which="DumpAllFace")

    args = argparser.parse_args()
    return args


def main(argv):
    cursor = init_cursor()

    args = args_parser(argv)
    if args.which == "GetAllUserInfo":
        result = args.function(cursor)
    elif args.which == "GetUserInfoById":
        result = args.function(cursor, args.id_user)
    elif args.which == "GetFace":
        result = args.function(
            cursor, args.id_user, args.id_person, args.offset, args.limit
        )
    elif args.which == "GetNamedAndHiddenPerson":
        result = args.function(cursor, args.id_user, args.offset, args.limit)
    elif args.which == "CountFaceNeedToMigrate":
        result = args.function(cursor, args.id_user)
    elif args.which == "GetTeamLibraryPath":
        result = args.function(cursor)
    elif args.which == "DumpAllFace":
        result = args.function(cursor, args.id_user)

    if args.output_path:
        path = args.output_path
        json.dump(result, open(path, "w"))
    else:
        print(json.dumps(result))

    return result


if __name__ == "__main__":
    ret = 0
    try:
        result = main(sys.argv[1:])
    except Exception as e:
        ret = 1
        logging.error(" ".join(sys.argv) + " failed!")
        print(e)
    sys.exit(ret)