supernote 0.13.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. supernote/__init__.py +28 -0
  2. supernote/alembic/__init__.py +0 -0
  3. supernote/alembic/env.py +121 -0
  4. supernote/alembic/script.py.mako +28 -0
  5. supernote/alembic/versions/0543a383957b_initial_schema.py +312 -0
  6. supernote/alembic.ini +149 -0
  7. supernote/cli/__init__.py +1 -0
  8. supernote/cli/admin.py +178 -0
  9. supernote/cli/client.py +434 -0
  10. supernote/cli/main.py +61 -0
  11. supernote/cli/notebook.py +313 -0
  12. supernote/cli/server.py +89 -0
  13. supernote/client/__init__.py +28 -0
  14. supernote/client/admin.py +76 -0
  15. supernote/client/api.py +135 -0
  16. supernote/client/api_model.py +231 -0
  17. supernote/client/auth.py +84 -0
  18. supernote/client/client.py +365 -0
  19. supernote/client/device.py +361 -0
  20. supernote/client/exceptions.py +33 -0
  21. supernote/client/extended.py +20 -0
  22. supernote/client/hashing.py +50 -0
  23. supernote/client/login_client.py +154 -0
  24. supernote/client/schedule.py +149 -0
  25. supernote/client/summary.py +156 -0
  26. supernote/client/web.py +213 -0
  27. supernote/models/__init__.py +14 -0
  28. supernote/models/auth.py +380 -0
  29. supernote/models/base.py +266 -0
  30. supernote/models/equipment.py +276 -0
  31. supernote/models/extended.py +124 -0
  32. supernote/models/file_common.py +95 -0
  33. supernote/models/file_device.py +571 -0
  34. supernote/models/file_web.py +602 -0
  35. supernote/models/schedule.py +663 -0
  36. supernote/models/summary.py +830 -0
  37. supernote/models/system.py +431 -0
  38. supernote/models/user.py +638 -0
  39. supernote/notebook/__init__.py +67 -0
  40. supernote/notebook/color.py +93 -0
  41. supernote/notebook/converter.py +560 -0
  42. supernote/notebook/decoder.py +398 -0
  43. supernote/notebook/exceptions.py +43 -0
  44. supernote/notebook/fileformat.py +463 -0
  45. supernote/notebook/manipulator.py +420 -0
  46. supernote/notebook/parser.py +737 -0
  47. supernote/notebook/utils.py +49 -0
  48. supernote/py.typed +0 -0
  49. supernote/server/__init__.py +12 -0
  50. supernote/server/app.py +428 -0
  51. supernote/server/config.py +265 -0
  52. supernote/server/constants.py +26 -0
  53. supernote/server/db/__init__.py +9 -0
  54. supernote/server/db/base.py +8 -0
  55. supernote/server/db/migrations.py +44 -0
  56. supernote/server/db/models/__init__.py +23 -0
  57. supernote/server/db/models/device.py +17 -0
  58. supernote/server/db/models/file.py +107 -0
  59. supernote/server/db/models/kv.py +17 -0
  60. supernote/server/db/models/login_record.py +22 -0
  61. supernote/server/db/models/note_processing.py +100 -0
  62. supernote/server/db/models/schedule.py +83 -0
  63. supernote/server/db/models/summary.py +122 -0
  64. supernote/server/db/models/user.py +30 -0
  65. supernote/server/db/session.py +88 -0
  66. supernote/server/events.py +63 -0
  67. supernote/server/exceptions.py +153 -0
  68. supernote/server/mcp/__init__.py +1 -0
  69. supernote/server/mcp/models.py +109 -0
  70. supernote/server/mcp/server.py +176 -0
  71. supernote/server/resources/__init__.py +0 -0
  72. supernote/server/resources/prompts/__init__.py +0 -0
  73. supernote/server/resources/prompts/ocr/common/context.md +19 -0
  74. supernote/server/resources/prompts/ocr/common/legend.md +11 -0
  75. supernote/server/resources/prompts/ocr/daily/prompt.md +12 -0
  76. supernote/server/resources/prompts/ocr/default/system.md +14 -0
  77. supernote/server/resources/prompts/ocr/monthly/prompt.md +22 -0
  78. supernote/server/resources/prompts/ocr/weekly/prompt.md +16 -0
  79. supernote/server/resources/prompts/summary/common/instruction.md +8 -0
  80. supernote/server/resources/prompts/summary/daily/prompt.md +6 -0
  81. supernote/server/resources/prompts/summary/default/prompt.md +4 -0
  82. supernote/server/resources/prompts/summary/monthly/prompt.md +6 -0
  83. supernote/server/resources/prompts/summary/weekly/prompt.md +6 -0
  84. supernote/server/routes/admin.py +109 -0
  85. supernote/server/routes/auth.py +337 -0
  86. supernote/server/routes/decorators.py +13 -0
  87. supernote/server/routes/extended.py +139 -0
  88. supernote/server/routes/file_device.py +593 -0
  89. supernote/server/routes/file_web.py +597 -0
  90. supernote/server/routes/oss.py +317 -0
  91. supernote/server/routes/schedule.py +223 -0
  92. supernote/server/routes/summary.py +390 -0
  93. supernote/server/routes/system.py +57 -0
  94. supernote/server/services/__init__.py +15 -0
  95. supernote/server/services/blob.py +214 -0
  96. supernote/server/services/coordination.py +171 -0
  97. supernote/server/services/file.py +970 -0
  98. supernote/server/services/gemini.py +66 -0
  99. supernote/server/services/integrity.py +112 -0
  100. supernote/server/services/processor.py +331 -0
  101. supernote/server/services/processor_modules/__init__.py +145 -0
  102. supernote/server/services/processor_modules/gemini_embedding.py +112 -0
  103. supernote/server/services/processor_modules/gemini_ocr.py +149 -0
  104. supernote/server/services/processor_modules/page_hashing.py +220 -0
  105. supernote/server/services/processor_modules/png_conversion.py +107 -0
  106. supernote/server/services/processor_modules/summary.py +302 -0
  107. supernote/server/services/prompt_loader.py +133 -0
  108. supernote/server/services/schedule.py +161 -0
  109. supernote/server/services/search.py +250 -0
  110. supernote/server/services/summary.py +419 -0
  111. supernote/server/services/user.py +480 -0
  112. supernote/server/services/vfs.py +476 -0
  113. supernote/server/static/favicon.ico +0 -0
  114. supernote/server/static/index.html +186 -0
  115. supernote/server/static/js/api/client.js +463 -0
  116. supernote/server/static/js/components/FileCard.js +55 -0
  117. supernote/server/static/js/components/FileViewer.js +137 -0
  118. supernote/server/static/js/components/LoginCard.js +64 -0
  119. supernote/server/static/js/components/MoveModal.js +74 -0
  120. supernote/server/static/js/components/RenameModal.js +39 -0
  121. supernote/server/static/js/components/SummaryPanel.js +101 -0
  122. supernote/server/static/js/components/SystemPanel.js +158 -0
  123. supernote/server/static/js/composables/useFileSystem.js +116 -0
  124. supernote/server/static/js/main.js +241 -0
  125. supernote/server/static/style.css +14 -0
  126. supernote/server/utils/hashing.py +18 -0
  127. supernote/server/utils/note_content.py +76 -0
  128. supernote/server/utils/paths.py +58 -0
  129. supernote/server/utils/rate_limit.py +52 -0
  130. supernote/server/utils/tasks.py +71 -0
  131. supernote/server/utils/unique_id.py +48 -0
  132. supernote/server/utils/url_signer.py +218 -0
  133. supernote-0.13.6.dist-info/METADATA +183 -0
  134. supernote-0.13.6.dist-info/RECORD +138 -0
  135. supernote-0.13.6.dist-info/WHEEL +5 -0
  136. supernote-0.13.6.dist-info/entry_points.txt +3 -0
  137. supernote-0.13.6.dist-info/licenses/LICENSE +201 -0
  138. supernote-0.13.6.dist-info/top_level.txt +1 -0
supernote/__init__.py ADDED
@@ -0,0 +1,28 @@
1
+ """
2
+ .. include:: ../README.md
3
+ """
4
+
5
+ # Core notebook parsing (always available)
6
+ from . import notebook # noqa: F401
7
+
8
+ __all__ = [
9
+ "models",
10
+ ]
11
+
12
+ # Optional: Client library
13
+ try:
14
+ from . import client # noqa: F401
15
+
16
+ __all__.extend(["client"])
17
+ except ImportError:
18
+ pass
19
+
20
+ # Optional: Server
21
+ try:
22
+ from . import server # noqa: F401
23
+
24
+ __all__.extend(["server"])
25
+ except ImportError:
26
+ pass
27
+
28
+ __all__.extend(["notebook"])
File without changes
@@ -0,0 +1,121 @@
1
+ import asyncio
2
+ import sys
3
+ from logging.config import fileConfig
4
+ from pathlib import Path
5
+
6
+ from alembic import context
7
+ from sqlalchemy import pool
8
+ from sqlalchemy.engine import Connection, engine_from_config
9
+ from sqlalchemy.ext.asyncio import async_engine_from_config
10
+
11
+ # Add the project root to sys.path so we can import supernote modules
12
+ sys.path.append(str(Path(__file__).parent.parent))
13
+
14
+ from supernote.server.config import ServerConfig
15
+ from supernote.server.db.base import Base
16
+
17
+ # Import all models so they are registered with Base.metadata
18
+ from supernote.server.db.models import * # noqa
19
+
20
+ # ----------------------------------------------------------------------
21
+
22
+ # this is the Alembic Config object, which provides
23
+ # access to the values within the .ini file in use.
24
+ config = context.config
25
+
26
+ # Interpret the config file for Python logging.
27
+ # This line sets up loggers basically.
28
+ if config.config_file_name is not None and not config.get_main_option(
29
+ "skip_logging_config"
30
+ ):
31
+ fileConfig(config.config_file_name, disable_existing_loggers=False)
32
+
33
+ # target_metadata is the MetaData object of your ORM base
34
+ target_metadata = Base.metadata
35
+
36
+ # Override sqlalchemy.url with the one from our application config
37
+ # We load the config without a file (defaults) or from env vars
38
+ # BUT we only do this if the config hasn't been set explicitly (e.g. by a test)
39
+ current_url = config.get_main_option("sqlalchemy.url")
40
+ if not current_url or "driver://" in current_url:
41
+ app_config = ServerConfig.load()
42
+ config.set_main_option("sqlalchemy.url", app_config.db_url)
43
+
44
+
45
+ def run_migrations_offline() -> None:
46
+ """Run migrations in 'offline' mode.
47
+
48
+ This configures the context with just a URL
49
+ and not an Engine, though an Engine is acceptable
50
+ here as well. By skipping the Engine creation
51
+ we don't even need a DBAPI to be available.
52
+
53
+ Calls to context.execute() here emit the given string to the
54
+ script output.
55
+
56
+ """
57
+ url = config.get_main_option("sqlalchemy.url")
58
+ context.configure(
59
+ url=url,
60
+ target_metadata=target_metadata,
61
+ literal_binds=True,
62
+ dialect_opts={"paramstyle": "named"},
63
+ )
64
+
65
+ with context.begin_transaction():
66
+ context.run_migrations()
67
+
68
+
69
+ def do_run_migrations(connection: Connection) -> None:
70
+ context.configure(connection=connection, target_metadata=target_metadata)
71
+
72
+ with context.begin_transaction():
73
+ context.run_migrations()
74
+
75
+
76
+ async def run_async_migrations() -> None:
77
+ """In this scenario we need to create an Engine
78
+ and associate a connection with the context.
79
+
80
+ """
81
+
82
+ connectable = async_engine_from_config(
83
+ config.get_section(config.config_ini_section, {}),
84
+ prefix="sqlalchemy.",
85
+ poolclass=pool.NullPool,
86
+ )
87
+
88
+ async with connectable.connect() as connection:
89
+ await connection.run_sync(do_run_migrations)
90
+
91
+ await connectable.dispose()
92
+
93
+
94
+ def run_migrations_online() -> None:
95
+ """Run migrations in 'online' mode."""
96
+
97
+ # Check if we are using an async driver
98
+ url = config.get_main_option("sqlalchemy.url")
99
+ assert url is not None
100
+ is_async = url.startswith("sqlite+aiosqlite") or url.startswith(
101
+ "postgresql+asyncpg"
102
+ )
103
+
104
+ if is_async:
105
+ asyncio.run(run_async_migrations())
106
+ else:
107
+ # Standard sync migration
108
+ connectable = engine_from_config(
109
+ config.get_section(config.config_ini_section, {}),
110
+ prefix="sqlalchemy.",
111
+ poolclass=pool.NullPool,
112
+ )
113
+
114
+ with connectable.connect() as connection:
115
+ do_run_migrations(connection)
116
+
117
+
118
+ if context.is_offline_mode():
119
+ run_migrations_offline()
120
+ else:
121
+ run_migrations_online()
@@ -0,0 +1,28 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ ${imports if imports else ""}
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = ${repr(up_revision)}
16
+ down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
17
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ ${upgrades if upgrades else "pass"}
24
+
25
+
26
+ def downgrade() -> None:
27
+ """Downgrade schema."""
28
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,312 @@
1
+ """Initial schema
2
+
3
+ Revision ID: 0543a383957b
4
+ Revises:
5
+ Create Date: 2026-01-19 19:21:12.802707
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = "0543a383957b"
16
+ down_revision: Union[str, Sequence[str], None] = None
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.create_table(
25
+ "f_capacity",
26
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
27
+ sa.Column("used_capacity", sa.BigInteger(), nullable=False),
28
+ sa.Column("total_capacity", sa.BigInteger(), nullable=False),
29
+ sa.PrimaryKeyConstraint("user_id"),
30
+ )
31
+ op.create_table(
32
+ "f_note_page_content",
33
+ sa.Column("id", sa.BigInteger(), nullable=False),
34
+ sa.Column("file_id", sa.BigInteger(), nullable=False),
35
+ sa.Column("page_index", sa.BigInteger(), nullable=False),
36
+ sa.Column("page_id", sa.String(), nullable=False),
37
+ sa.Column("content_hash", sa.String(), nullable=True),
38
+ sa.Column("text_content", sa.Text(), nullable=True),
39
+ sa.Column("embedding", sa.Text(), nullable=True),
40
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
41
+ sa.Column("update_time", sa.BigInteger(), nullable=False),
42
+ sa.PrimaryKeyConstraint("id"),
43
+ sa.UniqueConstraint("file_id", "page_id", name="uq_note_page_content"),
44
+ )
45
+ op.create_index(
46
+ op.f("ix_f_note_page_content_file_id"),
47
+ "f_note_page_content",
48
+ ["file_id"],
49
+ unique=False,
50
+ )
51
+ op.create_table(
52
+ "f_recycle_file",
53
+ sa.Column("id", sa.BigInteger(), nullable=False),
54
+ sa.Column("file_id", sa.BigInteger(), nullable=False),
55
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
56
+ sa.Column("file_name", sa.String(), nullable=False),
57
+ sa.Column("size", sa.BigInteger(), nullable=False),
58
+ sa.Column("is_folder", sa.String(length=1), nullable=False),
59
+ sa.Column("delete_time", sa.BigInteger(), nullable=False),
60
+ sa.PrimaryKeyConstraint("id"),
61
+ )
62
+ op.create_table(
63
+ "f_summary",
64
+ sa.Column("id", sa.BigInteger(), nullable=False),
65
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
66
+ sa.Column("file_id", sa.BigInteger(), nullable=True),
67
+ sa.Column("name", sa.String(), nullable=True),
68
+ sa.Column("unique_identifier", sa.String(), nullable=False),
69
+ sa.Column("parent_unique_identifier", sa.String(), nullable=True),
70
+ sa.Column("content", sa.Text(), nullable=True),
71
+ sa.Column("source_path", sa.String(), nullable=True),
72
+ sa.Column("data_source", sa.String(), nullable=True),
73
+ sa.Column("source_type", sa.BigInteger(), nullable=True),
74
+ sa.Column("is_summary_group", sa.Boolean(), nullable=False),
75
+ sa.Column("description", sa.String(), nullable=True),
76
+ sa.Column("tags", sa.String(), nullable=True),
77
+ sa.Column("md5_hash", sa.String(), nullable=True),
78
+ sa.Column("extra_metadata", sa.Text(), nullable=True),
79
+ sa.Column("comment_str", sa.String(), nullable=True),
80
+ sa.Column("comment_handwrite_name", sa.String(), nullable=True),
81
+ sa.Column("handwrite_inner_name", sa.String(), nullable=True),
82
+ sa.Column("handwrite_md5", sa.String(), nullable=True),
83
+ sa.Column("creation_time", sa.BigInteger(), nullable=True),
84
+ sa.Column("last_modified_time", sa.BigInteger(), nullable=True),
85
+ sa.Column("is_deleted", sa.Boolean(), nullable=False),
86
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
87
+ sa.Column("update_time", sa.BigInteger(), nullable=False),
88
+ sa.Column("author", sa.String(), nullable=True),
89
+ sa.PrimaryKeyConstraint("id"),
90
+ sa.UniqueConstraint("unique_identifier"),
91
+ )
92
+ op.create_index(
93
+ op.f("ix_f_summary_file_id"), "f_summary", ["file_id"], unique=False
94
+ )
95
+ op.create_index(
96
+ op.f("ix_f_summary_parent_unique_identifier"),
97
+ "f_summary",
98
+ ["parent_unique_identifier"],
99
+ unique=False,
100
+ )
101
+ op.create_index(
102
+ op.f("ix_f_summary_user_id"), "f_summary", ["user_id"], unique=False
103
+ )
104
+ op.create_table(
105
+ "f_summary_tag",
106
+ sa.Column("id", sa.BigInteger(), nullable=False),
107
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
108
+ sa.Column("name", sa.String(), nullable=False),
109
+ sa.Column("unique_identifier", sa.String(), nullable=False),
110
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
111
+ sa.PrimaryKeyConstraint("id"),
112
+ sa.UniqueConstraint("unique_identifier"),
113
+ )
114
+ op.create_index(
115
+ op.f("ix_f_summary_tag_user_id"), "f_summary_tag", ["user_id"], unique=False
116
+ )
117
+ op.create_table(
118
+ "f_system_task",
119
+ sa.Column("id", sa.BigInteger(), nullable=False),
120
+ sa.Column("file_id", sa.BigInteger(), nullable=False),
121
+ sa.Column("task_type", sa.String(), nullable=False),
122
+ sa.Column("key", sa.String(), nullable=False),
123
+ sa.Column("status", sa.String(), nullable=False),
124
+ sa.Column("data", sa.Text(), nullable=True),
125
+ sa.Column("retry_count", sa.BigInteger(), nullable=False),
126
+ sa.Column("last_error", sa.Text(), nullable=True),
127
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
128
+ sa.Column("update_time", sa.BigInteger(), nullable=False),
129
+ sa.PrimaryKeyConstraint("id"),
130
+ sa.UniqueConstraint("file_id", "task_type", "key", name="uq_system_task"),
131
+ )
132
+ op.create_index(
133
+ op.f("ix_f_system_task_file_id"), "f_system_task", ["file_id"], unique=False
134
+ )
135
+ op.create_index(
136
+ op.f("ix_f_system_task_task_type"), "f_system_task", ["task_type"], unique=False
137
+ )
138
+ op.create_table(
139
+ "f_user_file",
140
+ sa.Column("id", sa.BigInteger(), nullable=False),
141
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
142
+ sa.Column("directory_id", sa.BigInteger(), nullable=False),
143
+ sa.Column("file_name", sa.String(), nullable=False),
144
+ sa.Column("is_folder", sa.String(length=1), nullable=False),
145
+ sa.Column("size", sa.BigInteger(), nullable=False),
146
+ sa.Column("md5", sa.String(), nullable=True),
147
+ sa.Column("storage_key", sa.String(), nullable=True),
148
+ sa.Column("is_active", sa.String(length=1), nullable=False),
149
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
150
+ sa.Column("update_time", sa.BigInteger(), nullable=False),
151
+ sa.PrimaryKeyConstraint("id"),
152
+ )
153
+ op.create_index(
154
+ "idx_user_dir_active",
155
+ "f_user_file",
156
+ ["user_id", "directory_id", "is_active"],
157
+ unique=False,
158
+ )
159
+ op.create_index(
160
+ op.f("ix_f_user_file_directory_id"),
161
+ "f_user_file",
162
+ ["directory_id"],
163
+ unique=False,
164
+ )
165
+ op.create_index(
166
+ op.f("ix_f_user_file_user_id"), "f_user_file", ["user_id"], unique=False
167
+ )
168
+ op.create_table(
169
+ "key_values",
170
+ sa.Column("key", sa.String(), nullable=False),
171
+ sa.Column("value", sa.String(), nullable=False),
172
+ sa.Column("expiry", sa.Float(), nullable=False),
173
+ sa.PrimaryKeyConstraint("key"),
174
+ )
175
+ op.create_index(
176
+ op.f("ix_key_values_expiry"), "key_values", ["expiry"], unique=False
177
+ )
178
+ op.create_table(
179
+ "t_schedule_task",
180
+ sa.Column("task_id", sa.BigInteger(), nullable=False),
181
+ sa.Column("task_list_id", sa.BigInteger(), nullable=False),
182
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
183
+ sa.Column("title", sa.String(), nullable=False),
184
+ sa.Column("detail", sa.String(), nullable=True),
185
+ sa.Column("status", sa.String(), nullable=False),
186
+ sa.Column("importance", sa.String(), nullable=True),
187
+ sa.Column("due_time", sa.BigInteger(), nullable=True),
188
+ sa.Column("completed_time", sa.BigInteger(), nullable=True),
189
+ sa.Column("recurrence", sa.String(), nullable=True),
190
+ sa.Column("is_reminder_on", sa.Boolean(), nullable=False),
191
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
192
+ sa.Column("update_time", sa.BigInteger(), nullable=False),
193
+ sa.PrimaryKeyConstraint("task_id"),
194
+ )
195
+ op.create_index(
196
+ op.f("ix_t_schedule_task_task_list_id"),
197
+ "t_schedule_task",
198
+ ["task_list_id"],
199
+ unique=False,
200
+ )
201
+ op.create_index(
202
+ op.f("ix_t_schedule_task_user_id"), "t_schedule_task", ["user_id"], unique=False
203
+ )
204
+ op.create_table(
205
+ "t_schedule_task_group",
206
+ sa.Column("task_list_id", sa.BigInteger(), nullable=False),
207
+ sa.Column("user_id", sa.BigInteger(), nullable=False),
208
+ sa.Column("title", sa.String(), nullable=False),
209
+ sa.Column("create_time", sa.BigInteger(), nullable=False),
210
+ sa.PrimaryKeyConstraint("task_list_id"),
211
+ )
212
+ op.create_index(
213
+ op.f("ix_t_schedule_task_group_user_id"),
214
+ "t_schedule_task_group",
215
+ ["user_id"],
216
+ unique=False,
217
+ )
218
+ op.create_table(
219
+ "users",
220
+ sa.Column("id", sa.Integer(), nullable=False),
221
+ sa.Column("email", sa.String(), nullable=False),
222
+ sa.Column("password_md5", sa.String(), nullable=False),
223
+ sa.Column("is_active", sa.Boolean(), nullable=False),
224
+ sa.Column("display_name", sa.String(), nullable=True),
225
+ sa.Column("phone", sa.String(), nullable=True),
226
+ sa.Column("avatar", sa.String(), nullable=True),
227
+ sa.Column("total_capacity", sa.String(), nullable=False),
228
+ sa.Column("is_admin", sa.Boolean(), nullable=False),
229
+ sa.PrimaryKeyConstraint("id"),
230
+ )
231
+ op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
232
+ op.create_table(
233
+ "devices",
234
+ sa.Column("id", sa.Integer(), nullable=False),
235
+ sa.Column("user_id", sa.Integer(), nullable=False),
236
+ sa.Column("equipment_no", sa.String(), nullable=False),
237
+ sa.ForeignKeyConstraint(
238
+ ["user_id"],
239
+ ["users.id"],
240
+ ),
241
+ sa.PrimaryKeyConstraint("id"),
242
+ )
243
+ op.create_index(
244
+ op.f("ix_devices_equipment_no"), "devices", ["equipment_no"], unique=True
245
+ )
246
+ op.create_index(op.f("ix_devices_user_id"), "devices", ["user_id"], unique=False)
247
+ op.create_table(
248
+ "login_records",
249
+ sa.Column("id", sa.Integer(), nullable=False),
250
+ sa.Column("user_id", sa.Integer(), nullable=False),
251
+ sa.Column("login_method", sa.String(), nullable=True),
252
+ sa.Column("equipment", sa.String(), nullable=True),
253
+ sa.Column("ip", sa.String(), nullable=True),
254
+ sa.Column("create_time", sa.String(), nullable=False),
255
+ sa.ForeignKeyConstraint(
256
+ ["user_id"],
257
+ ["users.id"],
258
+ ),
259
+ sa.PrimaryKeyConstraint("id"),
260
+ )
261
+ op.create_index(
262
+ op.f("ix_login_records_create_time"),
263
+ "login_records",
264
+ ["create_time"],
265
+ unique=False,
266
+ )
267
+ op.create_index(
268
+ op.f("ix_login_records_user_id"), "login_records", ["user_id"], unique=False
269
+ )
270
+ # ### end Alembic commands ###
271
+
272
+
273
+ def downgrade() -> None:
274
+ """Downgrade schema."""
275
+ # ### commands auto generated by Alembic - please adjust! ###
276
+ op.drop_index(op.f("ix_login_records_user_id"), table_name="login_records")
277
+ op.drop_index(op.f("ix_login_records_create_time"), table_name="login_records")
278
+ op.drop_table("login_records")
279
+ op.drop_index(op.f("ix_devices_user_id"), table_name="devices")
280
+ op.drop_index(op.f("ix_devices_equipment_no"), table_name="devices")
281
+ op.drop_table("devices")
282
+ op.drop_index(op.f("ix_users_email"), table_name="users")
283
+ op.drop_table("users")
284
+ op.drop_index(
285
+ op.f("ix_t_schedule_task_group_user_id"), table_name="t_schedule_task_group"
286
+ )
287
+ op.drop_table("t_schedule_task_group")
288
+ op.drop_index(op.f("ix_t_schedule_task_user_id"), table_name="t_schedule_task")
289
+ op.drop_index(op.f("ix_t_schedule_task_task_list_id"), table_name="t_schedule_task")
290
+ op.drop_table("t_schedule_task")
291
+ op.drop_index(op.f("ix_key_values_expiry"), table_name="key_values")
292
+ op.drop_table("key_values")
293
+ op.drop_index(op.f("ix_f_user_file_user_id"), table_name="f_user_file")
294
+ op.drop_index(op.f("ix_f_user_file_directory_id"), table_name="f_user_file")
295
+ op.drop_index("idx_user_dir_active", table_name="f_user_file")
296
+ op.drop_table("f_user_file")
297
+ op.drop_index(op.f("ix_f_system_task_task_type"), table_name="f_system_task")
298
+ op.drop_index(op.f("ix_f_system_task_file_id"), table_name="f_system_task")
299
+ op.drop_table("f_system_task")
300
+ op.drop_index(op.f("ix_f_summary_tag_user_id"), table_name="f_summary_tag")
301
+ op.drop_table("f_summary_tag")
302
+ op.drop_index(op.f("ix_f_summary_user_id"), table_name="f_summary")
303
+ op.drop_index(op.f("ix_f_summary_parent_unique_identifier"), table_name="f_summary")
304
+ op.drop_index(op.f("ix_f_summary_file_id"), table_name="f_summary")
305
+ op.drop_table("f_summary")
306
+ op.drop_table("f_recycle_file")
307
+ op.drop_index(
308
+ op.f("ix_f_note_page_content_file_id"), table_name="f_note_page_content"
309
+ )
310
+ op.drop_table("f_note_page_content")
311
+ op.drop_table("f_capacity")
312
+ # ### end Alembic commands ###
supernote/alembic.ini ADDED
@@ -0,0 +1,149 @@
1
+ # A generic, single database configuration.
2
+
3
+ [alembic]
4
+ # path to migration scripts.
5
+ # this is typically a path given in POSIX (e.g. forward slashes)
6
+ # format, relative to the token %(here)s which refers to the location of this
7
+ # ini file
8
+ script_location = supernote:alembic
9
+
10
+ # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
11
+ # Uncomment the line below if you want the files to be prepended with date and time
12
+ # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
13
+ # for all available tokens
14
+ # file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
15
+ # Or organize into date-based subdirectories (requires recursive_version_locations = true)
16
+ # file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s
17
+
18
+ # sys.path path, will be prepended to sys.path if present.
19
+ # defaults to the current working directory. for multiple paths, the path separator
20
+ # is defined by "path_separator" below.
21
+ prepend_sys_path = .
22
+
23
+ # timezone to use when rendering the date within the migration file
24
+ # as well as the filename.
25
+ # If specified, requires the tzdata library which can be installed by adding
26
+ # `alembic[tz]` to the pip requirements.
27
+ # string value is passed to ZoneInfo()
28
+ # leave blank for localtime
29
+ # timezone =
30
+
31
+ # max length of characters to apply to the "slug" field
32
+ # truncate_slug_length = 40
33
+
34
+ # set to 'true' to run the environment during
35
+ # the 'revision' command, regardless of autogenerate
36
+ # revision_environment = false
37
+
38
+ # set to 'true' to allow .pyc and .pyo files without
39
+ # a source .py file to be detected as revisions in the
40
+ # versions/ directory
41
+ # sourceless = false
42
+
43
+ # version location specification; This defaults
44
+ # to <script_location>/versions. When using multiple version
45
+ # directories, initial revisions must be specified with --version-path.
46
+ # The path separator used here should be the separator specified by "path_separator"
47
+ # below.
48
+ # version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions
49
+
50
+ # path_separator; This indicates what character is used to split lists of file
51
+ # paths, including version_locations and prepend_sys_path within configparser
52
+ # files such as alembic.ini.
53
+ # The default rendered in new alembic.ini files is "os", which uses os.pathsep
54
+ # to provide os-dependent path splitting.
55
+ #
56
+ # Note that in order to support legacy alembic.ini files, this default does NOT
57
+ # take place if path_separator is not present in alembic.ini. If this
58
+ # option is omitted entirely, fallback logic is as follows:
59
+ #
60
+ # 1. Parsing of the version_locations option falls back to using the legacy
61
+ # "version_path_separator" key, which if absent then falls back to the legacy
62
+ # behavior of splitting on spaces and/or commas.
63
+ # 2. Parsing of the prepend_sys_path option falls back to the legacy
64
+ # behavior of splitting on spaces, commas, or colons.
65
+ #
66
+ # Valid values for path_separator are:
67
+ #
68
+ # path_separator = :
69
+ # path_separator = ;
70
+ # path_separator = space
71
+ # path_separator = newline
72
+ #
73
+ # Use os.pathsep. Default configuration used for new projects.
74
+ path_separator = os
75
+
76
+
77
+ # set to 'true' to search source files recursively
78
+ # in each "version_locations" directory
79
+ # new in Alembic version 1.10
80
+ # recursive_version_locations = false
81
+
82
+ # the output encoding used when revision files
83
+ # are written from script.py.mako
84
+ # output_encoding = utf-8
85
+
86
+ # database URL. This is consumed by the user-maintained env.py script only.
87
+ # other means of configuring database URLs may be customized within the env.py
88
+ # file.
89
+ sqlalchemy.url = driver://user:pass@localhost/dbname
90
+
91
+
92
+ [post_write_hooks]
93
+ # post_write_hooks defines scripts or Python functions that are run
94
+ # on newly generated revision scripts. See the documentation for further
95
+ # detail and examples
96
+
97
+ # format using "black" - use the console_scripts runner, against the "black" entrypoint
98
+ # hooks = black
99
+ # black.type = console_scripts
100
+ # black.entrypoint = black
101
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
102
+
103
+ # lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module
104
+ # hooks = ruff
105
+ # ruff.type = module
106
+ # ruff.module = ruff
107
+ # ruff.options = check --fix REVISION_SCRIPT_FILENAME
108
+
109
+ # Alternatively, use the exec runner to execute a binary found on your PATH
110
+ # hooks = ruff
111
+ # ruff.type = exec
112
+ # ruff.executable = ruff
113
+ # ruff.options = check --fix REVISION_SCRIPT_FILENAME
114
+
115
+ # Logging configuration. This is also consumed by the user-maintained
116
+ # env.py script only.
117
+ [loggers]
118
+ keys = root,sqlalchemy,alembic
119
+
120
+ [handlers]
121
+ keys = console
122
+
123
+ [formatters]
124
+ keys = generic
125
+
126
+ [logger_root]
127
+ level = WARNING
128
+ handlers = console
129
+ qualname =
130
+
131
+ [logger_sqlalchemy]
132
+ level = WARNING
133
+ handlers =
134
+ qualname = sqlalchemy.engine
135
+
136
+ [logger_alembic]
137
+ level = INFO
138
+ handlers =
139
+ qualname = alembic
140
+
141
+ [handler_console]
142
+ class = StreamHandler
143
+ args = (sys.stderr,)
144
+ level = NOTSET
145
+ formatter = generic
146
+
147
+ [formatter_generic]
148
+ format = %(levelname)-5.5s [%(name)s] %(message)s
149
+ datefmt = %H:%M:%S
@@ -0,0 +1 @@
1
+ """Supernote command line tool."""