# Path: animanager/datets.py
# def to_ts(date: datetime.date) -> float:
# def to_date(ts: float) -> datetime.date:
# 
# Path: animanager/sqlite/utils.py
# def upsert(db, table, key_cols, update_dict):
#     """Fabled upsert for SQLiteDB.
# 
#     Perform an upsert based on primary key.
# 
#     :param SQLiteDB db: database
#     :param str table: table to upsert into
#     :param str key_cols: name of key columns
#     :param dict update_dict: key-value pairs to upsert
# 
#     """
#     with db:
#         cur = db.cursor()
#         cur.execute(
#             'UPDATE {} SET {} WHERE {}'.format(
#                 table,
#                 ','.join(_sqlpformat(col) for col in update_dict.keys()),
#                 ' AND '.join(_sqlpformat(col) for col in key_cols),
#             ),
#             update_dict,
#         )
#         if db.changes() == 0:
#             keys, values = zip(*update_dict.items())
#             cur.execute(
#                 'INSERT INTO {} ({}) VALUES ({})'.format(
#                     table,
#                     ','.join(keys),
#                     ','.join('?' for _ in values)),
#                 values)
# 
# Path: animanager/db/query/eptype.py
# def get_eptype(db, name):
#     """Get episode type by name or id."""
#     return EpisodeTypes.from_db(db)[name]
# 
# Path: animanager/db/query/select.py
# ALL = 1
# 
# def lookup(
#         db,
#         aid: int,
#         fields: FieldsParam = ALL,
#         episode_fields: FieldsParam = (),
# ) -> Anime:
#     """Look up information for a single anime.
# 
#     :param fields: anime fields to get.  If ``None``, get all fields.
#     :param episode_fields: episode fields to get.
#         If ``None``, get all fields.  If empty, don't get episodes.
# 
#     """
#     return next(select(
#         db,
#         'aid=?',
#         (aid,),
#         fields=fields,
#         episode_fields=episode_fields,
#     ))
# 
# Path: animanager/db/query/status.py
# def cache_status(db, aid, force=False):
#     """Calculate and cache status for given anime.
# 
#     Don't do anything if status already exists and force is False.
# 
#     """
#     with db:
#         cur = db.cursor()
#         if not force:
#             # We don't do anything if we already have this aid in our
#             # cache.
#             cur.execute('SELECT 1 FROM cache_anime WHERE aid=?', (aid,))
#             if cur.fetchone() is not None:
#                 return
# 
#         # Retrieve information for determining complete.
#         cur.execute(
#             'SELECT episodecount, enddate FROM anime WHERE aid=?', (aid,))
#         row = cur.fetchone()
#         if row is None:
#             raise ValueError('aid provided does not exist')
#         episodecount, enddate = row
# 
#         # Select all regular episodes in ascending order.
#         cur.execute("""
#             SELECT number, user_watched FROM episode
#             WHERE aid=? AND type=?
#             ORDER BY number ASC
#             """, (aid, get_eptype(db, 'regular').id))
# 
#         # We find the last consecutive episode that is user_watched.
#         number = 0
#         for number, watched in cur:
#             # Once we find the first unwatched episode, we set the last
#             # consecutive watched episode to the previous episode (or 0).
#             if watched == 0:
#                 number -= 1
#                 break
#         # We store this in the cache.
#         set_status(db, aid, enddate and episodecount <= number, number)
# 
# def set_status(
#         db,
#         aid: int,
#         complete: Any,
#         watched_episodes: int,
# ) -> None:
#     """Set anime status."""
#     upsert(db, 'cache_anime', ['aid'], {
#         'aid': aid,
#         'complete': 1 if complete else 0,
#         'watched_episodes': watched_episodes,
#     })


import logging
from animanager import datets
from animanager.sqlite.utils import upsert
from .eptype import get_eptype
from .select import ALL, lookup
from .status import cache_status, set_status
    }
    upsert(db, 'episode', ['aid', 'type', 'number'], values)


def delete_episode(db, aid, episode):
    """Delete an episode."""
    db.cursor().execute(
        'DELETE FROM episode WHERE aid=:aid AND type=:type AND number=:number',
        {
            'aid': aid,
            'type': episode.type,
            'number': episode.number,
        })


def set_watched(db, aid, ep_type, number):
    """Set episode as watched."""
    db.cursor().execute(
        """UPDATE episode SET user_watched=:watched
        WHERE aid=:aid AND type=:type AND number=:number""",
        {
            'aid': aid,
            'type': ep_type,
            'number': number,
            'watched': 1,
        })


def bump(db, aid):
    """Bump anime regular episode count."""

# Path: animanager/datets.py
# def to_ts(date: datetime.date) -> float:
# def to_date(ts: float) -> datetime.date:
# 
# Path: animanager/sqlite/utils.py
# def upsert(db, table, key_cols, update_dict):
#     """Fabled upsert for SQLiteDB.
# 
#     Perform an upsert based on primary key.
# 
#     :param SQLiteDB db: database
#     :param str table: table to upsert into
#     :param str key_cols: name of key columns
#     :param dict update_dict: key-value pairs to upsert
# 
#     """
#     with db:
#         cur = db.cursor()
#         cur.execute(
#             'UPDATE {} SET {} WHERE {}'.format(
#                 table,
#                 ','.join(_sqlpformat(col) for col in update_dict.keys()),
#                 ' AND '.join(_sqlpformat(col) for col in key_cols),
#             ),
#             update_dict,
#         )
#         if db.changes() == 0:
#             keys, values = zip(*update_dict.items())
#             cur.execute(
#                 'INSERT INTO {} ({}) VALUES ({})'.format(
#                     table,
#                     ','.join(keys),
#                     ','.join('?' for _ in values)),
#                 values)
# 
# Path: animanager/db/query/eptype.py
# def get_eptype(db, name):
#     """Get episode type by name or id."""
#     return EpisodeTypes.from_db(db)[name]
# 
# Path: animanager/db/query/select.py
# ALL = 1
# 
# def lookup(
#         db,
#         aid: int,
#         fields: FieldsParam = ALL,
#         episode_fields: FieldsParam = (),
# ) -> Anime:
#     """Look up information for a single anime.
# 
#     :param fields: anime fields to get.  If ``None``, get all fields.
#     :param episode_fields: episode fields to get.
#         If ``None``, get all fields.  If empty, don't get episodes.
# 
#     """
#     return next(select(
#         db,
#         'aid=?',
#         (aid,),
#         fields=fields,
#         episode_fields=episode_fields,
#     ))
# 
# Path: animanager/db/query/status.py
# def cache_status(db, aid, force=False):
#     """Calculate and cache status for given anime.
# 
#     Don't do anything if status already exists and force is False.
# 
#     """
#     with db:
#         cur = db.cursor()
#         if not force:
#             # We don't do anything if we already have this aid in our
#             # cache.
#             cur.execute('SELECT 1 FROM cache_anime WHERE aid=?', (aid,))
#             if cur.fetchone() is not None:
#                 return
# 
#         # Retrieve information for determining complete.
#         cur.execute(
#             'SELECT episodecount, enddate FROM anime WHERE aid=?', (aid,))
#         row = cur.fetchone()
#         if row is None:
#             raise ValueError('aid provided does not exist')
#         episodecount, enddate = row
# 
#         # Select all regular episodes in ascending order.
#         cur.execute("""
#             SELECT number, user_watched FROM episode
#             WHERE aid=? AND type=?
#             ORDER BY number ASC
#             """, (aid, get_eptype(db, 'regular').id))
# 
#         # We find the last consecutive episode that is user_watched.
#         number = 0
#         for number, watched in cur:
#             # Once we find the first unwatched episode, we set the last
#             # consecutive watched episode to the previous episode (or 0).
#             if watched == 0:
#                 number -= 1
#                 break
#         # We store this in the cache.
#         set_status(db, aid, enddate and episodecount <= number, number)
# 
# def set_status(
#         db,
#         aid: int,
#         complete: Any,
#         watched_episodes: int,
# ) -> None:
#     """Set anime status."""
#     upsert(db, 'cache_anime', ['aid'], {
#         'aid': aid,
#         'complete': 1 if complete else 0,
#         'watched_episodes': watched_episodes,
#     })


# Path: animanager/db/query/update.py
import logging
from animanager import datets
from animanager.sqlite.utils import upsert
from .eptype import get_eptype
from .select import ALL, lookup
from .status import cache_status, set_status

    }
    upsert(db, 'episode', ['aid', 'type', 'number'], values)


def delete_episode(db, aid, episode):
    """Delete an episode."""
    db.cursor().execute(
        'DELETE FROM episode WHERE aid=:aid AND type=:type AND number=:number',
        {
            'aid': aid,
            'type': episode.type,
            'number': episode.number,
        })


def set_watched(db, aid, ep_type, number):
    """Set episode as watched."""
    db.cursor().execute(
        """UPDATE episode SET user_watched=:watched
        WHERE aid=:aid AND type=:type AND number=:number""",
        {
            'aid': aid,
            'type': ep_type,
            'number': number,
            'watched': 1,
        })


def bump(db, aid):
    """Bump anime regular episode count."""

    anime = lookup(db, aid)