Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pauliyobo committed Sep 20, 2024
1 parent 4a1cf9e commit 0c2303d
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 34 deletions.
2 changes: 1 addition & 1 deletion bookworm/alembic.ini → alembic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[alembic]
# path to migration scripts
# Use forward slashes (/) also on windows to provide an os agnostic path
script_location = alembic
script_location = bookworm/alembic

# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
Expand Down
Empty file added bookworm/alembic/__init__.py
Empty file.
18 changes: 9 additions & 9 deletions bookworm/alembic/env.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import create_engine
from sqlalchemy import pool
from sqlalchemy.orm import configure_mappers

from alembic import context

from bookworm.database import Base, get_db_url


# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
Expand All @@ -18,7 +22,7 @@
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None
target_metadata = Base.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
Expand All @@ -38,7 +42,7 @@ def run_migrations_offline() -> None:
script output.
"""
url = config.get_main_option("sqlalchemy.url")
url = get_db_url()
context.configure(
url=url,
target_metadata=target_metadata,
Expand All @@ -57,12 +61,8 @@ def run_migrations_online() -> None:
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)

connectable = create_engine(get_db_url())

with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
Expand Down
104 changes: 104 additions & 0 deletions bookworm/alembic/versions/f3c35e4b6b3a_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""empty message
Revision ID: f3c35e4b6b3a
Revises:
Create Date: 2024-09-21 00:34:51.831112
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'f3c35e4b6b3a'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index('ix_note_tag_title', table_name='note_tag')
op.drop_table('note_tag')
op.drop_table('quotes_tags')
op.drop_table('note')
op.drop_index('ix_quote_tag_title', table_name='quote_tag')
op.drop_table('quote_tag')
op.drop_table('notes_tags')
op.drop_table('quote')
op.drop_table('bookmark')
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('bookmark',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('title', sa.VARCHAR(length=255), nullable=False),
sa.Column('page_number', sa.INTEGER(), nullable=False),
sa.Column('position', sa.INTEGER(), nullable=False),
sa.Column('section_title', sa.VARCHAR(length=1024), nullable=False),
sa.Column('section_identifier', sa.VARCHAR(length=1024), nullable=False),
sa.Column('date_created', sa.DATETIME(), nullable=True),
sa.Column('date_updated', sa.DATETIME(), nullable=True),
sa.Column('book_id', sa.INTEGER(), nullable=False),
sa.ForeignKeyConstraint(['book_id'], ['book.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('quote',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('title', sa.VARCHAR(length=255), nullable=False),
sa.Column('page_number', sa.INTEGER(), nullable=False),
sa.Column('position', sa.INTEGER(), nullable=False),
sa.Column('section_title', sa.VARCHAR(length=1024), nullable=False),
sa.Column('section_identifier', sa.VARCHAR(length=1024), nullable=False),
sa.Column('date_created', sa.DATETIME(), nullable=True),
sa.Column('date_updated', sa.DATETIME(), nullable=True),
sa.Column('start_pos', sa.INTEGER(), nullable=False),
sa.Column('end_pos', sa.INTEGER(), nullable=False),
sa.Column('content', sa.TEXT(), nullable=False),
sa.Column('book_id', sa.INTEGER(), nullable=False),
sa.ForeignKeyConstraint(['book_id'], ['book.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('notes_tags',
sa.Column('note_id', sa.INTEGER(), nullable=True),
sa.Column('note_tag_id', sa.INTEGER(), nullable=True),
sa.ForeignKeyConstraint(['note_id'], ['note.id'], ),
sa.ForeignKeyConstraint(['note_tag_id'], ['note_tag.id'], )
)
op.create_table('quote_tag',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('title', sa.VARCHAR(length=512), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index('ix_quote_tag_title', 'quote_tag', ['title'], unique=1)
op.create_table('note',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('title', sa.VARCHAR(length=255), nullable=False),
sa.Column('page_number', sa.INTEGER(), nullable=False),
sa.Column('position', sa.INTEGER(), nullable=False),
sa.Column('section_title', sa.VARCHAR(length=1024), nullable=False),
sa.Column('section_identifier', sa.VARCHAR(length=1024), nullable=False),
sa.Column('date_created', sa.DATETIME(), nullable=True),
sa.Column('date_updated', sa.DATETIME(), nullable=True),
sa.Column('content', sa.TEXT(), nullable=False),
sa.Column('book_id', sa.INTEGER(), nullable=False),
sa.ForeignKeyConstraint(['book_id'], ['book.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('quotes_tags',
sa.Column('quote_id', sa.INTEGER(), nullable=True),
sa.Column('quote_tag_id', sa.INTEGER(), nullable=True),
sa.ForeignKeyConstraint(['quote_id'], ['quote.id'], ),
sa.ForeignKeyConstraint(['quote_tag_id'], ['quote_tag.id'], )
)
op.create_table('note_tag',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('title', sa.VARCHAR(length=512), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index('ix_note_tag_title', 'note_tag', ['title'], unique=1)
# ### end Alembic commands ###
31 changes: 18 additions & 13 deletions bookworm/annotation/annotation_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import deferred, relationship, synonym

from bookworm.database import GetOrCreateMixin, db
from bookworm.database import GetOrCreateMixin, Base


class TaggedMixin:
Expand All @@ -30,7 +30,7 @@ class and all it's subclasses.
def _prepare_association_table(table_name, remote1, remote2):
return sa.Table(
table_name,
db.Model.metadata,
Base.metadata,
sa.Column(f"{remote1}_id", sa.Integer, sa.ForeignKey(f"{remote1}.id")),
sa.Column(f"{remote2}_id", sa.Integer, sa.ForeignKey(f"{remote2}.id")),
)
Expand All @@ -41,15 +41,15 @@ def tags(cls):
# Create the Tag model
tag_attrs = {
"id": sa.Column(sa.Integer, primary_key=True),
"title": db.string(512, nullable=False, unique=True, index=True),
"title": sa.Column(sa.String(512), nullable=False, unique=True, index=True),
"items": relationship(
cls,
secondary=lambda: cls.__tags_association_table__,
backref="related_tags",
),
}
cls.Tag = type(
f"{cls.__name__}Tag", (GetOrCreateMixin, db.Model), tag_attrs
f"{cls.__name__}Tag", (GetOrCreateMixin, Base), tag_attrs
)
# The many-to-many association table
cls.__tags_association_table__ = cls._prepare_association_table(
Expand All @@ -64,13 +64,14 @@ def tags(cls):
)


class AnnotationBase(db.Model):
class AnnotationBase(Base):
__abstract__ = True
title = db.string(255, nullable=False)
page_number = db.integer(nullable=False)
position = db.integer(nullable=False, default=0)
section_title = db.string(1024, nullable=False)
section_identifier = db.string(1024, nullable=False)
__tablename__ = "annotation_base"
title = sa.Column(sa.String(255), nullable=False)
page_number = sa.Column(sa.Integer, nullable=False)
position = sa.Column(sa.Integer, nullable=False, default=0)
section_title = sa.Column(sa.String(1024), nullable=False)
section_identifier = sa.Column(sa.String(1024), nullable=False)
date_created = sa.Column(sa.DateTime, default=datetime.utcnow)
date_updated = sa.Column(sa.DateTime, onupdate=datetime.utcnow)

Expand All @@ -90,14 +91,16 @@ def book(cls):

class Bookmark(AnnotationBase):
"""Represents a user-defined bookmark."""
__tablename__ = "bookmark"


class TaggedContent(AnnotationBase, TaggedMixin):
__abstract__ = True
__tablename__ = "tagged_content"

@declared_attr
def content(cls):
return deferred(db.text(nullable=False))
return deferred(sa.Column(sa.Text, nullable=False))

@declared_attr
def text_column(cls):
Expand All @@ -106,10 +109,12 @@ def text_column(cls):

class Note(TaggedContent):
"""Represents user comments (notes)."""
__tablename__ = "note"


class Quote(TaggedContent):
"""Represents a highlight (quote) from the book."""
__tablename__ = "quote"

start_pos = db.integer(nullable=False)
end_pos = db.integer(nullable=False)
start_pos = sa.Column(sa.Integer, nullable=False)
end_pos = sa.Column(sa.Integer, nullable=False)
15 changes: 12 additions & 3 deletions bookworm/database/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
import sqlite3

import db_magic as db
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

from bookworm.logger import logger
from bookworm.paths import db_path as get_db_path

from .models import (
Book,
Base,
DocumentPositionInfo,
GetOrCreateMixin,
PinnedDocument,
Expand All @@ -23,8 +26,14 @@

log = logger.getChild(__name__)

def get_db_url() -> str:
db_path = os.path.join(get_db_path(), "database.sqlite")
return f"sqlite:///{db_path}"

def init_database():
db_path = os.path.join(get_db_path(), "database.sqlite")
db.Model.setup_database(f"sqlite:///{db_path}", create=True)
upgrade_database_schema(db.Model.session)
engine = create_engine(get_db_url())
Base.metadata.create_all(engine)
Base.session = scoped_session(
sessionmaker(engine, autocommit=False, autoflush=False)
)

29 changes: 21 additions & 8 deletions bookworm/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import sqlalchemy as sa
from sqlalchemy import types
from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
from sqlalchemy.orm import DeclarativeBase, scoped_session, declarative_base

from bookworm.document.uri import DocumentUri
from bookworm.logger import logger
Expand Down Expand Up @@ -39,10 +40,17 @@ def get_or_create(cls, **kwargs):
return obj
return cls(**kwargs)

class Model:
id = sa.Column(sa.Integer, primary_key=True)

class DocumentBase(db.Model, GetOrCreateMixin):
Base = declarative_base()


class DocumentBase(Base, GetOrCreateMixin):
__abstract__ = True
title = db.string(512, nullable=False)
__tablename__ = "document_base"
id = sa.Column(sa.Integer, primary_key=True)
title = sa.Column(sa.String(512), nullable=False)
uri = sa.Column(DocumentUriDBType(1024), nullable=False, unique=True, index=True)

@classmethod
Expand All @@ -55,14 +63,17 @@ def get_or_create(cls, *args, **kwargs):


class Book(DocumentBase):
__tablename__ = "book"

@property
def identifier(self):
return self.uri.to_uri_string()


class DocumentPositionInfo(DocumentBase):
last_page = db.integer(default=0)
last_position = db.integer(default=0)
__tablename__ = "document_position_info"
last_page = sa.Column(sa.Integer, default=0)
last_position = sa.Column(sa.Integer, default=0)

def get_last_position(self):
return (self.last_page, self.last_position)
Expand All @@ -74,7 +85,8 @@ def save_position(self, page, pos):


class RecentDocument(DocumentBase):
last_opened_on = db.date_time(default=datetime.utcnow, onupdate=datetime.utcnow)
__tablename__ = "recent_document"
last_opened_on = sa.Column(sa.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

def record_open(self):
self.last_opened_on = datetime.utcnow()
Expand All @@ -93,9 +105,10 @@ def clear_all(cls):


class PinnedDocument(DocumentBase):
last_opened_on = db.date_time(default=datetime.utcnow, onupdate=datetime.utcnow)
is_pinned = db.boolean(default=False)
pinning_order = db.integer(default=0)
__tablename__ = "pinned_document"
last_opened_on = sa.Column(sa.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
is_pinned = sa.Column(sa.Boolean, default=False)
pinning_order = sa.Column(sa.Integer, default=0)

@classmethod
def get_pinned(cls, limit=50):
Expand Down

0 comments on commit 0c2303d

Please sign in to comment.