sort of working, more changes
This commit is contained in:
@ -17,3 +17,5 @@ MYSQL_ROOT_PASSWORD=supersecret
|
||||
NEO4J_URI=bolt://neo4j:7687
|
||||
NEO4J_USER=neo4j
|
||||
NEO4J_PASSWORD=your_secure_password
|
||||
|
||||
STANDARD_IMG_SIZE=300x200
|
||||
|
@ -1,5 +1,8 @@
|
||||
import os
|
||||
|
||||
# Define basedir so it’s available inside the Config class
|
||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
class Config:
|
||||
SECRET_KEY = os.environ['SECRET_KEY']
|
||||
UPLOAD_FOLDER = os.environ['UPLOAD_FOLDER']
|
||||
@ -28,3 +31,11 @@ class Config:
|
||||
NEO4J_URI = os.getenv('NEO4J_URI', 'bolt://neo4j:7687')
|
||||
NEO4J_USER = os.getenv('NEO4J_USER', 'neo4j')
|
||||
NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD', 'your_secure_password')
|
||||
|
||||
# Override or default upload folder
|
||||
UPLOAD_FOLDER = os.path.join(basedir, "static", "uploads")
|
||||
|
||||
# Standard image size (for placeholders, etc.)
|
||||
STANDARD_IMG_SIZE = tuple(
|
||||
map(int, os.getenv('STANDARD_IMG_SIZE', '300x200').split('x'))
|
||||
)
|
||||
|
BIN
main-app-new.zip
Normal file
BIN
main-app-new.zip
Normal file
Binary file not shown.
BIN
main-app.zip
BIN
main-app.zip
Binary file not shown.
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 0fcf1e150ae2
|
||||
Revises: 58516c9892e9
|
||||
Create Date: 2025-06-05 09:31:44.116783
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0fcf1e150ae2'
|
||||
down_revision = '58516c9892e9'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,8 +1,8 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 07d152ee2ac2
|
||||
Revises: 0171b270afc1
|
||||
Create Date: 2025-06-04 06:24:51.986909
|
||||
Revision ID: 13e8b68e0737
|
||||
Revises: 3065b811b58f
|
||||
Create Date: 2025-06-09 08:05:24.660884
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
@ -10,8 +10,8 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '07d152ee2ac2'
|
||||
down_revision = '0171b270afc1'
|
||||
revision = '13e8b68e0737'
|
||||
down_revision = '3065b811b58f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 1c7cef84b4ae
|
||||
Revises: 26803929dc3e
|
||||
Create Date: 2025-06-04 22:07:43.375613
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1c7cef84b4ae'
|
||||
down_revision = '26803929dc3e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('plant', sa.Column('is_verified', sa.Boolean(), nullable=False))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('plant', 'is_verified')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 1cd7fa3f84ce
|
||||
Revises: e0afd892f86e
|
||||
Create Date: 2025-06-09 02:46:59.792016
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1cd7fa3f84ce'
|
||||
down_revision = 'e0afd892f86e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 1edc2e2c93cd
|
||||
Revises: d11c2e8b173a
|
||||
Create Date: 2025-06-09 04:36:51.035371
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1edc2e2c93cd'
|
||||
down_revision = 'd11c2e8b173a'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,8 +1,8 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 00991befbf1d
|
||||
Revises: 1cd7fa3f84ce
|
||||
Create Date: 2025-06-09 02:54:23.747908
|
||||
Revision ID: 1f5b1e0b6b05
|
||||
Revises: 3426fe15f0ce
|
||||
Create Date: 2025-06-09 09:41:17.949317
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
@ -10,8 +10,8 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '00991befbf1d'
|
||||
down_revision = '1cd7fa3f84ce'
|
||||
revision = '1f5b1e0b6b05'
|
||||
down_revision = '3426fe15f0ce'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 246f5cce6c15
|
||||
Revises: 90401e0dbe75
|
||||
Create Date: 2025-06-09 05:22:52.461981
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '246f5cce6c15'
|
||||
down_revision = '90401e0dbe75'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 263b128622a9
|
||||
Revises: 00991befbf1d
|
||||
Create Date: 2025-06-09 04:06:48.275595
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '263b128622a9'
|
||||
down_revision = '00991befbf1d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 26803929dc3e
|
||||
Revises: 07d152ee2ac2
|
||||
Create Date: 2025-06-04 06:38:27.377036
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '26803929dc3e'
|
||||
down_revision = '07d152ee2ac2'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 27a65a4e055c
|
||||
Revises: 48d93714beaf
|
||||
Create Date: 2025-06-05 04:23:44.796455
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '27a65a4e055c'
|
||||
down_revision = '48d93714beaf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 2a0b02a42543
|
||||
Revises: 93b893e47742
|
||||
Create Date: 2025-06-05 02:41:56.741133
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2a0b02a42543'
|
||||
down_revision = '93b893e47742'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 2fa6feb17477
|
||||
Revises: 9cff183551e1
|
||||
Create Date: 2025-06-05 00:45:39.693560
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2fa6feb17477'
|
||||
down_revision = '9cff183551e1'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,8 +1,8 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 0171b270afc1
|
||||
Revises: 4d9859ada63b
|
||||
Create Date: 2025-06-04 06:20:47.463202
|
||||
Revision ID: 3065b811b58f
|
||||
Revises: fa22b011d450
|
||||
Create Date: 2025-06-09 07:41:07.546689
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
@ -10,8 +10,8 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0171b270afc1'
|
||||
down_revision = '4d9859ada63b'
|
||||
revision = '3065b811b58f'
|
||||
down_revision = 'fa22b011d450'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 01e2d617ae49
|
||||
Revises: 460dbe73c1bc
|
||||
Create Date: 2025-06-09 04:23:50.730127
|
||||
Revision ID: 33e98411843d
|
||||
Revises: c10353a20277
|
||||
Create Date: 2025-06-09 09:30:42.712274
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
@ -10,8 +10,8 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '01e2d617ae49'
|
||||
down_revision = '460dbe73c1bc'
|
||||
revision = '33e98411843d'
|
||||
down_revision = 'c10353a20277'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
28
migrations/versions/3426fe15f0ce_auto.py
Normal file
28
migrations/versions/3426fe15f0ce_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 3426fe15f0ce
|
||||
Revises: 33e98411843d
|
||||
Create Date: 2025-06-09 09:34:33.556990
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3426fe15f0ce'
|
||||
down_revision = '33e98411843d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,70 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 373571dfe134
|
||||
Revises: 0fcf1e150ae2
|
||||
Create Date: 2025-06-05 09:38:55.414193
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '373571dfe134'
|
||||
down_revision = '0fcf1e150ae2'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('submission_images', sa.Column('file_url', sa.String(length=256), nullable=False))
|
||||
op.add_column('submission_images', sa.Column('uploaded_at', sa.DateTime(), nullable=True))
|
||||
op.drop_column('submission_images', 'is_visible')
|
||||
op.drop_column('submission_images', 'file_path')
|
||||
op.add_column('submissions', sa.Column('submitted_at', sa.DateTime(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('plant_name', sa.String(length=100), nullable=False))
|
||||
op.add_column('submissions', sa.Column('approved', sa.Boolean(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('approved_at', sa.DateTime(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('reviewed_by', sa.Integer(), nullable=True))
|
||||
op.drop_constraint(op.f('submissions_ibfk_1'), 'submissions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'submissions', 'users', ['reviewed_by'], ['id'])
|
||||
op.drop_column('submissions', 'common_name')
|
||||
op.drop_column('submissions', 'height')
|
||||
op.drop_column('submissions', 'container_size')
|
||||
op.drop_column('submissions', 'timestamp')
|
||||
op.drop_column('submissions', 'price')
|
||||
op.drop_column('submissions', 'plant_id')
|
||||
op.drop_column('submissions', 'width')
|
||||
op.drop_column('submissions', 'health_status')
|
||||
op.drop_column('submissions', 'leaf_count')
|
||||
op.drop_column('submissions', 'potting_mix')
|
||||
op.drop_column('submissions', 'source')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('submissions', sa.Column('source', mysql.VARCHAR(length=120), nullable=True))
|
||||
op.add_column('submissions', sa.Column('potting_mix', mysql.VARCHAR(length=255), nullable=True))
|
||||
op.add_column('submissions', sa.Column('leaf_count', mysql.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('submissions', sa.Column('health_status', mysql.VARCHAR(length=50), nullable=True))
|
||||
op.add_column('submissions', sa.Column('width', mysql.FLOAT(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('plant_id', mysql.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('submissions', sa.Column('price', mysql.FLOAT(), nullable=False))
|
||||
op.add_column('submissions', sa.Column('timestamp', mysql.DATETIME(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('container_size', mysql.VARCHAR(length=120), nullable=True))
|
||||
op.add_column('submissions', sa.Column('height', mysql.FLOAT(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('common_name', mysql.VARCHAR(length=120), nullable=False))
|
||||
op.drop_constraint(None, 'submissions', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('submissions_ibfk_1'), 'submissions', 'plant', ['plant_id'], ['id'])
|
||||
op.drop_column('submissions', 'reviewed_by')
|
||||
op.drop_column('submissions', 'approved_at')
|
||||
op.drop_column('submissions', 'approved')
|
||||
op.drop_column('submissions', 'plant_name')
|
||||
op.drop_column('submissions', 'submitted_at')
|
||||
op.add_column('submission_images', sa.Column('file_path', mysql.VARCHAR(length=255), nullable=False))
|
||||
op.add_column('submission_images', sa.Column('is_visible', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True))
|
||||
op.drop_column('submission_images', 'uploaded_at')
|
||||
op.drop_column('submission_images', 'file_url')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 39f714eda2bf
|
||||
Revises: 6420e024f896
|
||||
Create Date: 2025-06-06 09:50:29.954004
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '39f714eda2bf'
|
||||
down_revision = '6420e024f896'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 3a0b96235583
|
||||
Revises: 263b128622a9
|
||||
Create Date: 2025-06-09 04:13:37.036064
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3a0b96235583'
|
||||
down_revision = '263b128622a9'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 401f262d79cc
|
||||
Revises: 583fab3f9f80
|
||||
Create Date: 2025-06-05 04:48:49.440383
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '401f262d79cc'
|
||||
down_revision = '583fab3f9f80'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 408c432b5835
|
||||
Revises: 77087ff2442e
|
||||
Create Date: 2025-06-06 08:34:26.804782
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '408c432b5835'
|
||||
down_revision = '77087ff2442e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,36 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 447ff559592b
|
||||
Revises: 408c432b5835
|
||||
Create Date: 2025-06-06 08:47:25.908940
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '447ff559592b'
|
||||
down_revision = '408c432b5835'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('submissions', sa.Column('vendor_name', sa.String(length=255), nullable=True))
|
||||
op.add_column('submissions', sa.Column('rating', sa.Integer(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('old_vendor', sa.String(length=255), nullable=True))
|
||||
op.add_column('submissions', sa.Column('new_vendor', sa.String(length=255), nullable=True))
|
||||
op.add_column('submissions', sa.Column('alias_reason', sa.Text(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('submissions', 'alias_reason')
|
||||
op.drop_column('submissions', 'new_vendor')
|
||||
op.drop_column('submissions', 'old_vendor')
|
||||
op.drop_column('submissions', 'rating')
|
||||
op.drop_column('submissions', 'vendor_name')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 460dbe73c1bc
|
||||
Revises: 3a0b96235583
|
||||
Create Date: 2025-06-09 04:16:30.838677
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '460dbe73c1bc'
|
||||
down_revision = '3a0b96235583'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 48d93714beaf
|
||||
Revises: 761d0f8be3ff
|
||||
Create Date: 2025-06-05 04:20:31.030479
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '48d93714beaf'
|
||||
down_revision = '761d0f8be3ff'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 48fee8a8a3be
|
||||
Revises: af76c66c9075
|
||||
Create Date: 2025-06-05 00:25:55.439874
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '48fee8a8a3be'
|
||||
down_revision = 'af76c66c9075'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 4bdec754b085
|
||||
Revises: 27a65a4e055c
|
||||
Create Date: 2025-06-05 04:34:19.085549
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4bdec754b085'
|
||||
down_revision = '27a65a4e055c'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 501b54868875
|
||||
Revises: 401f262d79cc
|
||||
Create Date: 2025-06-05 04:51:52.183453
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '501b54868875'
|
||||
down_revision = '401f262d79cc'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 50d5ff358f96
|
||||
Revises: 1c7cef84b4ae
|
||||
Create Date: 2025-06-04 22:14:54.902029
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '50d5ff358f96'
|
||||
down_revision = '1c7cef84b4ae'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/52e805a0163e_auto.py
Normal file
28
migrations/versions/52e805a0163e_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 52e805a0163e
|
||||
Revises: cb57ad0a3231
|
||||
Create Date: 2025-06-09 09:48:19.311607
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '52e805a0163e'
|
||||
down_revision = 'cb57ad0a3231'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/539c103a1ac4_auto.py
Normal file
28
migrations/versions/539c103a1ac4_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 539c103a1ac4
|
||||
Revises: b5b29b5b85ae
|
||||
Create Date: 2025-06-09 10:32:37.666108
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '539c103a1ac4'
|
||||
down_revision = 'b5b29b5b85ae'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 58022b5ab921
|
||||
Revises: 50d5ff358f96
|
||||
Create Date: 2025-06-04 22:32:06.203591
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '58022b5ab921'
|
||||
down_revision = '50d5ff358f96'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 583fab3f9f80
|
||||
Revises: 64ec4065d18d
|
||||
Create Date: 2025-06-05 04:47:05.679772
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '583fab3f9f80'
|
||||
down_revision = '64ec4065d18d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 58516c9892e9
|
||||
Revises: 85da58851d35
|
||||
Create Date: 2025-06-05 05:28:30.947641
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '58516c9892e9'
|
||||
down_revision = '85da58851d35'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 5c85ebc9451b
|
||||
Revises: d8bfe4d4c083
|
||||
Create Date: 2025-06-05 09:47:14.478039
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5c85ebc9451b'
|
||||
down_revision = 'd8bfe4d4c083'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 6420e024f896
|
||||
Revises: 7d232205181b
|
||||
Create Date: 2025-06-06 09:40:37.498453
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6420e024f896'
|
||||
down_revision = '7d232205181b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,44 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 64c1927562cc
|
||||
Revises: fb0243eaa7c3
|
||||
Create Date: 2025-06-06 07:54:00.147383
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '64c1927562cc'
|
||||
down_revision = 'fb0243eaa7c3'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('media', sa.Column('plant_id', sa.Integer(), nullable=True))
|
||||
op.add_column('media', sa.Column('update_id', sa.Integer(), nullable=True))
|
||||
op.drop_constraint(op.f('media_ibfk_2'), 'media', type_='foreignkey')
|
||||
op.drop_constraint(op.f('media_ibfk_3'), 'media', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'media', 'plant', ['plant_id'], ['id'])
|
||||
op.create_foreign_key(None, 'media', 'plant_updates', ['update_id'], ['id'])
|
||||
op.drop_column('media', 'uploader_id')
|
||||
op.drop_column('media', 'submission_id')
|
||||
op.drop_column('media', 'caption')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('media', sa.Column('caption', mysql.VARCHAR(length=255), nullable=True))
|
||||
op.add_column('media', sa.Column('submission_id', mysql.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('media', sa.Column('uploader_id', mysql.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'media', type_='foreignkey')
|
||||
op.drop_constraint(None, 'media', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('media_ibfk_3'), 'media', 'submissions', ['submission_id'], ['id'])
|
||||
op.create_foreign_key(op.f('media_ibfk_2'), 'media', 'users', ['uploader_id'], ['id'])
|
||||
op.drop_column('media', 'update_id')
|
||||
op.drop_column('media', 'plant_id')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 64ec4065d18d
|
||||
Revises: 4bdec754b085
|
||||
Create Date: 2025-06-05 04:40:02.186807
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '64ec4065d18d'
|
||||
down_revision = '4bdec754b085'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 6539ef5f5419
|
||||
Revises: 39f714eda2bf
|
||||
Create Date: 2025-06-06 10:03:52.256341
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6539ef5f5419'
|
||||
down_revision = '39f714eda2bf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 6cb1c3054071
|
||||
Revises: f4987441cc85
|
||||
Create Date: 2025-06-06 09:09:55.403015
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6cb1c3054071'
|
||||
down_revision = 'f4987441cc85'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/6fcf5e1ad9fa_auto.py
Normal file
28
migrations/versions/6fcf5e1ad9fa_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 6fcf5e1ad9fa
|
||||
Revises: a7883990430e
|
||||
Create Date: 2025-06-09 10:27:44.541187
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6fcf5e1ad9fa'
|
||||
down_revision = 'a7883990430e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,46 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 72455429fdaf
|
||||
Revises: 501b54868875
|
||||
Create Date: 2025-06-05 05:07:43.605568
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '72455429fdaf'
|
||||
down_revision = '501b54868875'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('transfer_request',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=False),
|
||||
sa.Column('seller_id', sa.Integer(), nullable=False),
|
||||
sa.Column('buyer_id', sa.Integer(), nullable=False),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('seller_message', sa.String(length=512), nullable=True),
|
||||
sa.Column('buyer_message', sa.String(length=512), nullable=True),
|
||||
sa.ForeignKeyConstraint(['buyer_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.ForeignKeyConstraint(['seller_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.add_column('plant', sa.Column('data_verified', sa.Boolean(), nullable=False))
|
||||
op.drop_column('plant_ownership_log', 'graph_node_id')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('plant_ownership_log', sa.Column('graph_node_id', mysql.VARCHAR(length=255), nullable=True))
|
||||
op.drop_column('plant', 'data_verified')
|
||||
op.drop_table('transfer_request')
|
||||
# ### end Alembic commands ###
|
@ -1,93 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 761d0f8be3ff
|
||||
Revises: ad9ea9d31b58
|
||||
Create Date: 2025-06-05 04:18:09.403526
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '761d0f8be3ff'
|
||||
down_revision = 'ad9ea9d31b58'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('plant_lineage')
|
||||
op.add_column('plant', sa.Column('updated_at', sa.DateTime(), nullable=True))
|
||||
op.drop_column('plant', 'transferred')
|
||||
op.drop_column('plant', 'status')
|
||||
op.drop_column('plant', 'is_verified')
|
||||
op.drop_column('plant', 'graph_node_id')
|
||||
op.drop_column('plant', 'notes')
|
||||
op.add_column('plant_common_name', sa.Column('created_at', sa.DateTime(), nullable=True))
|
||||
op.alter_column('plant_common_name', 'name',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
type_=sa.String(length=128),
|
||||
existing_nullable=False)
|
||||
op.add_column('plant_ownership_log', sa.Column('date_acquired', sa.DateTime(), nullable=True))
|
||||
op.add_column('plant_ownership_log', sa.Column('transferred', sa.Boolean(), nullable=False))
|
||||
op.add_column('plant_ownership_log', sa.Column('graph_node_id', sa.String(length=255), nullable=True))
|
||||
op.add_column('plant_ownership_log', sa.Column('is_verified', sa.Boolean(), nullable=False))
|
||||
op.drop_column('plant_ownership_log', 'start_time')
|
||||
op.drop_column('plant_ownership_log', 'transfer_note')
|
||||
op.drop_column('plant_ownership_log', 'end_time')
|
||||
op.add_column('plant_scientific_name', sa.Column('created_at', sa.DateTime(), nullable=True))
|
||||
op.alter_column('plant_scientific_name', 'name',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
type_=sa.String(length=256),
|
||||
existing_nullable=False)
|
||||
op.alter_column('tag', 'name',
|
||||
existing_type=mysql.VARCHAR(length=255),
|
||||
type_=sa.String(length=128),
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('tag', 'name',
|
||||
existing_type=sa.String(length=128),
|
||||
type_=mysql.VARCHAR(length=255),
|
||||
existing_nullable=False)
|
||||
op.alter_column('plant_scientific_name', 'name',
|
||||
existing_type=sa.String(length=256),
|
||||
type_=mysql.VARCHAR(length=255),
|
||||
existing_nullable=False)
|
||||
op.drop_column('plant_scientific_name', 'created_at')
|
||||
op.add_column('plant_ownership_log', sa.Column('end_time', mysql.DATETIME(), nullable=True))
|
||||
op.add_column('plant_ownership_log', sa.Column('transfer_note', mysql.TEXT(), nullable=True))
|
||||
op.add_column('plant_ownership_log', sa.Column('start_time', mysql.DATETIME(), nullable=False))
|
||||
op.drop_column('plant_ownership_log', 'is_verified')
|
||||
op.drop_column('plant_ownership_log', 'graph_node_id')
|
||||
op.drop_column('plant_ownership_log', 'transferred')
|
||||
op.drop_column('plant_ownership_log', 'date_acquired')
|
||||
op.alter_column('plant_common_name', 'name',
|
||||
existing_type=sa.String(length=128),
|
||||
type_=mysql.VARCHAR(length=255),
|
||||
existing_nullable=False)
|
||||
op.drop_column('plant_common_name', 'created_at')
|
||||
op.add_column('plant', sa.Column('notes', mysql.TEXT(), nullable=True))
|
||||
op.add_column('plant', sa.Column('graph_node_id', mysql.VARCHAR(length=255), nullable=True))
|
||||
op.add_column('plant', sa.Column('is_verified', mysql.TINYINT(display_width=1), autoincrement=False, nullable=False))
|
||||
op.add_column('plant', sa.Column('status', mysql.VARCHAR(length=50), nullable=False))
|
||||
op.add_column('plant', sa.Column('transferred', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True))
|
||||
op.drop_column('plant', 'updated_at')
|
||||
op.create_table('plant_lineage',
|
||||
sa.Column('id', mysql.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('child_plant_id', mysql.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('parent_plant_id', mysql.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.Column('type', mysql.VARCHAR(length=50), nullable=False),
|
||||
sa.ForeignKeyConstraint(['child_plant_id'], ['plant.id'], name=op.f('plant_lineage_ibfk_1')),
|
||||
sa.ForeignKeyConstraint(['parent_plant_id'], ['plant.id'], name=op.f('plant_lineage_ibfk_2')),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_collate='utf8mb4_0900_ai_ci',
|
||||
mysql_default_charset='utf8mb4',
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
# ### end Alembic commands ###
|
@ -1,38 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 77087ff2442e
|
||||
Revises: 64c1927562cc
|
||||
Create Date: 2025-06-06 08:10:58.028201
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '77087ff2442e'
|
||||
down_revision = '64c1927562cc'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('media', sa.Column('uploader_id', sa.Integer(), nullable=False))
|
||||
op.add_column('media', sa.Column('caption', sa.String(length=255), nullable=True))
|
||||
op.create_foreign_key(None, 'media', 'users', ['uploader_id'], ['id'])
|
||||
op.add_column('submissions', sa.Column('submission_type', sa.String(length=50), nullable=False))
|
||||
op.add_column('submissions', sa.Column('price', sa.Float(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('source', sa.String(length=255), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('submissions', 'source')
|
||||
op.drop_column('submissions', 'price')
|
||||
op.drop_column('submissions', 'submission_type')
|
||||
op.drop_constraint(None, 'media', type_='foreignkey')
|
||||
op.drop_column('media', 'caption')
|
||||
op.drop_column('media', 'uploader_id')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 7d232205181b
|
||||
Revises: fad6fe2b5e43
|
||||
Create Date: 2025-06-06 09:38:39.786953
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7d232205181b'
|
||||
down_revision = 'fad6fe2b5e43'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/7d9fc95edc61_auto.py
Normal file
28
migrations/versions/7d9fc95edc61_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 7d9fc95edc61
|
||||
Revises: fa3de05c91fb
|
||||
Create Date: 2025-06-09 10:10:23.833551
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7d9fc95edc61'
|
||||
down_revision = 'fa3de05c91fb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 7dbb6d550055
|
||||
Revises: 72455429fdaf
|
||||
Create Date: 2025-06-05 05:10:43.392181
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7dbb6d550055'
|
||||
down_revision = '72455429fdaf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 7dd0c2491053
|
||||
Revises: 6539ef5f5419
|
||||
Create Date: 2025-06-09 02:26:02.002280
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7dd0c2491053'
|
||||
down_revision = '6539ef5f5419'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 806e94a40aeb
|
||||
Revises: e1cdc5f78f5e
|
||||
Create Date: 2025-06-05 01:11:25.968741
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '806e94a40aeb'
|
||||
down_revision = 'e1cdc5f78f5e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/80cf84342c5f_auto.py
Normal file
28
migrations/versions/80cf84342c5f_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 80cf84342c5f
|
||||
Revises: 539c103a1ac4
|
||||
Create Date: 2025-06-09 10:35:15.685799
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '80cf84342c5f'
|
||||
down_revision = '539c103a1ac4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 85da58851d35
|
||||
Revises: 8cd29b8fb6ec
|
||||
Create Date: 2025-06-05 05:20:46.638884
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '85da58851d35'
|
||||
down_revision = '8cd29b8fb6ec'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 8605e1ff50cd
|
||||
Revises: cab9b8b4d05b
|
||||
Create Date: 2025-06-09 04:53:58.830485
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '8605e1ff50cd'
|
||||
down_revision = 'cab9b8b4d05b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 8cd29b8fb6ec
|
||||
Revises: 7dbb6d550055
|
||||
Create Date: 2025-06-05 05:12:50.608338
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '8cd29b8fb6ec'
|
||||
down_revision = '7dbb6d550055'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 90401e0dbe75
|
||||
Revises: 8605e1ff50cd
|
||||
Create Date: 2025-06-09 05:15:46.404716
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '90401e0dbe75'
|
||||
down_revision = '8605e1ff50cd'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 93b893e47742
|
||||
Revises: b783b3b43713
|
||||
Create Date: 2025-06-05 02:37:12.714926
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '93b893e47742'
|
||||
down_revision = 'b783b3b43713'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 9b93a2dffe81
|
||||
Revises: c1a4158c8226
|
||||
Create Date: 2025-06-05 01:29:51.402975
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '9b93a2dffe81'
|
||||
down_revision = 'c1a4158c8226'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 9cff183551e1
|
||||
Revises: 48fee8a8a3be
|
||||
Create Date: 2025-06-05 00:32:07.995675
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '9cff183551e1'
|
||||
down_revision = '48fee8a8a3be'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/9d73ac427e40_auto.py
Normal file
28
migrations/versions/9d73ac427e40_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 9d73ac427e40
|
||||
Revises: b9234524f710
|
||||
Create Date: 2025-06-09 08:23:23.453209
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '9d73ac427e40'
|
||||
down_revision = 'b9234524f710'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/a10cbbbeb3f6_auto.py
Normal file
28
migrations/versions/a10cbbbeb3f6_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: a10cbbbeb3f6
|
||||
Revises: 9d73ac427e40
|
||||
Create Date: 2025-06-09 08:28:08.962286
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a10cbbbeb3f6'
|
||||
down_revision = '9d73ac427e40'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/a7883990430e_auto.py
Normal file
28
migrations/versions/a7883990430e_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: a7883990430e
|
||||
Revises: 7d9fc95edc61
|
||||
Create Date: 2025-06-09 10:13:51.730708
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a7883990430e'
|
||||
down_revision = '7d9fc95edc61'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/ab1a71750f4e_auto.py
Normal file
28
migrations/versions/ab1a71750f4e_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: ab1a71750f4e
|
||||
Revises: 52e805a0163e
|
||||
Create Date: 2025-06-09 09:50:46.848952
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ab1a71750f4e'
|
||||
down_revision = '52e805a0163e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: ad9ea9d31b58
|
||||
Revises: 2a0b02a42543
|
||||
Create Date: 2025-06-05 03:05:30.311725
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ad9ea9d31b58'
|
||||
down_revision = '2a0b02a42543'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: af76c66c9075
|
||||
Revises: 58022b5ab921
|
||||
Create Date: 2025-06-04 22:44:12.056714
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'af76c66c9075'
|
||||
down_revision = '58022b5ab921'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/b5b29b5b85ae_auto.py
Normal file
28
migrations/versions/b5b29b5b85ae_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: b5b29b5b85ae
|
||||
Revises: 6fcf5e1ad9fa
|
||||
Create Date: 2025-06-09 10:30:56.308436
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b5b29b5b85ae'
|
||||
down_revision = '6fcf5e1ad9fa'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: b783b3b43713
|
||||
Revises: bfc7a6bd8abc
|
||||
Create Date: 2025-06-05 02:07:18.572162
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b783b3b43713'
|
||||
down_revision = 'bfc7a6bd8abc'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/b9234524f710_auto.py
Normal file
28
migrations/versions/b9234524f710_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: b9234524f710
|
||||
Revises: 13e8b68e0737
|
||||
Create Date: 2025-06-09 08:19:19.133720
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b9234524f710'
|
||||
down_revision = '13e8b68e0737'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: b9c03e1ae0bf
|
||||
Revises: 9b93a2dffe81
|
||||
Create Date: 2025-06-05 01:37:57.483736
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b9c03e1ae0bf'
|
||||
down_revision = '9b93a2dffe81'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: bfc7a6bd8abc
|
||||
Revises: cc35036a6f94
|
||||
Create Date: 2025-06-05 01:57:23.973531
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'bfc7a6bd8abc'
|
||||
down_revision = 'cc35036a6f94'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/c10353a20277_auto.py
Normal file
28
migrations/versions/c10353a20277_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: c10353a20277
|
||||
Revises: e4ece621c461
|
||||
Create Date: 2025-06-09 09:13:23.016684
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c10353a20277'
|
||||
down_revision = 'e4ece621c461'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: c1a4158c8226
|
||||
Revises: 806e94a40aeb
|
||||
Create Date: 2025-06-05 01:16:54.451574
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c1a4158c8226'
|
||||
down_revision = '806e94a40aeb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: c9495b058ab0
|
||||
Revises: 373571dfe134
|
||||
Create Date: 2025-06-05 09:42:35.228096
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c9495b058ab0'
|
||||
down_revision = '373571dfe134'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: cab9b8b4d05b
|
||||
Revises: 1edc2e2c93cd
|
||||
Create Date: 2025-06-09 04:42:01.258302
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'cab9b8b4d05b'
|
||||
down_revision = '1edc2e2c93cd'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/cb3ce762cabb_auto.py
Normal file
28
migrations/versions/cb3ce762cabb_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: cb3ce762cabb
|
||||
Revises: ab1a71750f4e
|
||||
Create Date: 2025-06-09 09:57:52.586507
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'cb3ce762cabb'
|
||||
down_revision = 'ab1a71750f4e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/cb57ad0a3231_auto.py
Normal file
28
migrations/versions/cb57ad0a3231_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: cb57ad0a3231
|
||||
Revises: 1f5b1e0b6b05
|
||||
Create Date: 2025-06-09 09:44:00.832472
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'cb57ad0a3231'
|
||||
down_revision = '1f5b1e0b6b05'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: cc35036a6f94
|
||||
Revises: b9c03e1ae0bf
|
||||
Create Date: 2025-06-05 01:45:09.251040
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'cc35036a6f94'
|
||||
down_revision = 'b9c03e1ae0bf'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: d11c2e8b173a
|
||||
Revises: 01e2d617ae49
|
||||
Create Date: 2025-06-09 04:31:08.076159
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd11c2e8b173a'
|
||||
down_revision = '01e2d617ae49'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: d8bfe4d4c083
|
||||
Revises: c9495b058ab0
|
||||
Create Date: 2025-06-05 09:44:47.740029
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd8bfe4d4c083'
|
||||
down_revision = 'c9495b058ab0'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,37 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: e0afd892f86e
|
||||
Revises: 7dd0c2491053
|
||||
Create Date: 2025-06-09 02:37:06.190352
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e0afd892f86e'
|
||||
down_revision = '7dd0c2491053'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('import_batches',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('export_id', sa.String(length=64), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('imported_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('export_id', 'user_id', name='uix_export_user')
|
||||
)
|
||||
op.create_index(op.f('ix_import_batches_user_id'), 'import_batches', ['user_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_import_batches_user_id'), table_name='import_batches')
|
||||
op.drop_table('import_batches')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: e1cdc5f78f5e
|
||||
Revises: 2fa6feb17477
|
||||
Create Date: 2025-06-05 00:57:10.914714
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e1cdc5f78f5e'
|
||||
down_revision = '2fa6feb17477'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,70 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: e34cff15a95e
|
||||
Revises: 5c85ebc9451b
|
||||
Create Date: 2025-06-06 07:34:33.699976
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e34cff15a95e'
|
||||
down_revision = '5c85ebc9451b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('featured_images', sa.Column('media_id', sa.Integer(), nullable=False))
|
||||
op.drop_constraint(op.f('featured_images_ibfk_1'), 'featured_images', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'featured_images', 'media', ['media_id'], ['id'])
|
||||
op.drop_column('featured_images', 'submission_image_id')
|
||||
op.add_column('image_hearts', sa.Column('media_id', sa.Integer(), nullable=False))
|
||||
op.drop_constraint(op.f('image_hearts_ibfk_1'), 'image_hearts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'image_hearts', 'media', ['media_id'], ['id'])
|
||||
op.drop_column('image_hearts', 'submission_image_id')
|
||||
op.add_column('media', sa.Column('uploader_id', sa.Integer(), nullable=False))
|
||||
op.add_column('media', sa.Column('submission_id', sa.Integer(), nullable=True))
|
||||
op.drop_constraint(op.f('media_ibfk_2'), 'media', type_='foreignkey')
|
||||
op.drop_constraint(op.f('media_ibfk_3'), 'media', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'media', 'users', ['uploader_id'], ['id'])
|
||||
op.create_foreign_key(None, 'media', 'submissions', ['submission_id'], ['id'])
|
||||
op.drop_column('media', 'update_id')
|
||||
op.drop_column('media', 'plant_id')
|
||||
op.add_column('submissions', sa.Column('submission_type', sa.String(length=50), nullable=False))
|
||||
op.add_column('submissions', sa.Column('price', sa.Float(), nullable=True))
|
||||
op.add_column('submissions', sa.Column('source', sa.String(length=255), nullable=True))
|
||||
op.alter_column('submissions', 'plant_name',
|
||||
existing_type=mysql.VARCHAR(length=100),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('submissions', 'plant_name',
|
||||
existing_type=mysql.VARCHAR(length=100),
|
||||
nullable=False)
|
||||
op.drop_column('submissions', 'source')
|
||||
op.drop_column('submissions', 'price')
|
||||
op.drop_column('submissions', 'submission_type')
|
||||
op.add_column('media', sa.Column('plant_id', mysql.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('media', sa.Column('update_id', mysql.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.drop_constraint(None, 'media', type_='foreignkey')
|
||||
op.drop_constraint(None, 'media', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('media_ibfk_3'), 'media', 'plant_updates', ['update_id'], ['id'])
|
||||
op.create_foreign_key(op.f('media_ibfk_2'), 'media', 'plant', ['plant_id'], ['id'])
|
||||
op.drop_column('media', 'submission_id')
|
||||
op.drop_column('media', 'uploader_id')
|
||||
op.add_column('image_hearts', sa.Column('submission_image_id', mysql.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'image_hearts', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('image_hearts_ibfk_1'), 'image_hearts', 'submission_images', ['submission_image_id'], ['id'])
|
||||
op.drop_column('image_hearts', 'media_id')
|
||||
op.add_column('featured_images', sa.Column('submission_image_id', mysql.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'featured_images', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('featured_images_ibfk_1'), 'featured_images', 'submission_images', ['submission_image_id'], ['id'])
|
||||
op.drop_column('featured_images', 'media_id')
|
||||
# ### end Alembic commands ###
|
28
migrations/versions/e4ece621c461_auto.py
Normal file
28
migrations/versions/e4ece621c461_auto.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: e4ece621c461
|
||||
Revises: a10cbbbeb3f6
|
||||
Create Date: 2025-06-09 08:55:16.262879
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e4ece621c461'
|
||||
down_revision = 'a10cbbbeb3f6'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('media', sa.Column('created_at', sa.DateTime(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('media', 'created_at')
|
||||
# ### end Alembic commands ###
|
@ -1,28 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: f4987441cc85
|
||||
Revises: 447ff559592b
|
||||
Create Date: 2025-06-06 08:59:55.024371
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f4987441cc85'
|
||||
down_revision = '447ff559592b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
pass
|
||||
# ### end Alembic commands ###
|
@ -1,8 +1,8 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: 4d9859ada63b
|
||||
Revision ID: fa22b011d450
|
||||
Revises:
|
||||
Create Date: 2025-06-04 06:16:08.829142
|
||||
Create Date: 2025-06-09 06:59:45.406606
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
@ -10,7 +10,7 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4d9859ada63b'
|
||||
revision = 'fa22b011d450'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
@ -18,18 +18,26 @@ depends_on = None
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('import_batches',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('export_id', sa.String(length=64), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('imported_at', sa.DateTime(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('export_id', 'user_id', name='uix_export_user')
|
||||
)
|
||||
op.create_index(op.f('ix_import_batches_user_id'), 'import_batches', ['user_id'], unique=False)
|
||||
op.create_table('plant_common_name',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('name', sa.String(length=128), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('tag',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('name', sa.String(length=128), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('users',
|
||||
@ -41,18 +49,39 @@ def upgrade():
|
||||
sa.Column('excluded_from_analytics', sa.Boolean(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email'),
|
||||
sa.UniqueConstraint('email')
|
||||
)
|
||||
op.create_table('plant_scientific_name',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('name', sa.String(length=256), nullable=False),
|
||||
sa.Column('common_id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['common_id'], ['plant_common_name.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('submissions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('submitted_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('plant_name', sa.String(length=100), nullable=True),
|
||||
sa.Column('scientific_name', sa.String(length=120), nullable=True),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('submission_type', sa.String(length=50), nullable=False),
|
||||
sa.Column('price', sa.Float(), nullable=True),
|
||||
sa.Column('source', sa.String(length=255), nullable=True),
|
||||
sa.Column('vendor_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('rating', sa.Integer(), nullable=True),
|
||||
sa.Column('old_vendor', sa.String(length=255), nullable=True),
|
||||
sa.Column('new_vendor', sa.String(length=255), nullable=True),
|
||||
sa.Column('alias_reason', sa.Text(), nullable=True),
|
||||
sa.Column('approved', sa.Boolean(), nullable=True),
|
||||
sa.Column('approved_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('reviewed_by', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['reviewed_by'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('plant',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('uuid', sa.String(length=36), nullable=False),
|
||||
@ -61,20 +90,24 @@ def upgrade():
|
||||
sa.Column('common_id', sa.Integer(), nullable=False),
|
||||
sa.Column('scientific_id', sa.Integer(), nullable=False),
|
||||
sa.Column('plant_type', sa.String(length=50), nullable=False),
|
||||
sa.Column('status', sa.String(length=50), nullable=False),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('transferred', sa.Boolean(), nullable=True),
|
||||
sa.Column('graph_node_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('data_verified', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['common_id'], ['plant_common_name.id'], ),
|
||||
sa.ForeignKeyConstraint(['owner_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['scientific_id'], ['plant_scientific_name.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('custom_slug'),
|
||||
sa.UniqueConstraint('custom_slug'),
|
||||
sa.UniqueConstraint('uuid'),
|
||||
sa.UniqueConstraint('uuid')
|
||||
)
|
||||
op.create_table('submission_images',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('submission_id', sa.Integer(), nullable=False),
|
||||
sa.Column('file_url', sa.String(length=256), nullable=False),
|
||||
sa.Column('uploaded_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['submission_id'], ['submissions.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('grow_logs',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=False),
|
||||
@ -83,22 +116,13 @@ def upgrade():
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('plant_lineage',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('child_plant_id', sa.Integer(), nullable=False),
|
||||
sa.Column('parent_plant_id', sa.Integer(), nullable=False),
|
||||
sa.Column('type', sa.String(length=50), nullable=False),
|
||||
sa.ForeignKeyConstraint(['child_plant_id'], ['plant.id'], ),
|
||||
sa.ForeignKeyConstraint(['parent_plant_id'], ['plant.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('plant_ownership_log',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('start_time', sa.DateTime(), nullable=False),
|
||||
sa.Column('end_time', sa.DateTime(), nullable=True),
|
||||
sa.Column('transfer_note', sa.Text(), nullable=True),
|
||||
sa.Column('date_acquired', sa.DateTime(), nullable=True),
|
||||
sa.Column('transferred', sa.Boolean(), nullable=False),
|
||||
sa.Column('is_verified', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
@ -110,24 +134,19 @@ def upgrade():
|
||||
sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ),
|
||||
sa.PrimaryKeyConstraint('plant_id', 'tag_id')
|
||||
)
|
||||
op.create_table('submissions',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=True),
|
||||
sa.Column('common_name', sa.String(length=120), nullable=False),
|
||||
sa.Column('scientific_name', sa.String(length=120), nullable=True),
|
||||
sa.Column('price', sa.Float(), nullable=False),
|
||||
sa.Column('source', sa.String(length=120), nullable=True),
|
||||
sa.Column('timestamp', sa.DateTime(), nullable=True),
|
||||
sa.Column('height', sa.Float(), nullable=True),
|
||||
sa.Column('width', sa.Float(), nullable=True),
|
||||
sa.Column('leaf_count', sa.Integer(), nullable=True),
|
||||
sa.Column('potting_mix', sa.String(length=255), nullable=True),
|
||||
sa.Column('container_size', sa.String(length=120), nullable=True),
|
||||
sa.Column('health_status', sa.String(length=50), nullable=True),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
op.create_table('transfer_request',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=False),
|
||||
sa.Column('seller_id', sa.Integer(), nullable=False),
|
||||
sa.Column('buyer_id', sa.Integer(), nullable=False),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('seller_message', sa.String(length=512), nullable=True),
|
||||
sa.Column('buyer_message', sa.String(length=512), nullable=True),
|
||||
sa.ForeignKeyConstraint(['buyer_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['seller_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('plant_updates',
|
||||
@ -141,62 +160,58 @@ def upgrade():
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('submission_images',
|
||||
op.create_table('media',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('submission_id', sa.Integer(), nullable=False),
|
||||
sa.Column('file_path', sa.String(length=255), nullable=False),
|
||||
sa.Column('is_visible', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['submission_id'], ['submissions.id'], ),
|
||||
sa.Column('file_url', sa.String(length=256), nullable=False),
|
||||
sa.Column('uploaded_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('uploader_id', sa.Integer(), nullable=False),
|
||||
sa.Column('caption', sa.String(length=255), nullable=True),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=True),
|
||||
sa.Column('growlog_id', sa.Integer(), nullable=True),
|
||||
sa.Column('update_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['growlog_id'], ['grow_logs.id'], ),
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.ForeignKeyConstraint(['update_id'], ['plant_updates.id'], ),
|
||||
sa.ForeignKeyConstraint(['uploader_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('featured_images',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('submission_image_id', sa.Integer(), nullable=False),
|
||||
sa.Column('media_id', sa.Integer(), nullable=False),
|
||||
sa.Column('override_text', sa.String(length=255), nullable=True),
|
||||
sa.Column('is_featured', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['submission_image_id'], ['submission_images.id'], ),
|
||||
sa.ForeignKeyConstraint(['media_id'], ['media.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('image_hearts',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('submission_image_id', sa.Integer(), nullable=False),
|
||||
sa.Column('media_id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['submission_image_id'], ['submission_images.id'], ),
|
||||
sa.ForeignKeyConstraint(['media_id'], ['media.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('media',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('file_url', sa.String(length=256), nullable=False),
|
||||
sa.Column('uploaded_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('plant_id', sa.Integer(), nullable=True),
|
||||
sa.Column('growlog_id', sa.Integer(), nullable=True),
|
||||
sa.Column('update_id', sa.Integer(), nullable=True),
|
||||
sa.Column('caption', sa.String(length=255), nullable=True),
|
||||
sa.ForeignKeyConstraint(['growlog_id'], ['grow_logs.id'], ),
|
||||
sa.ForeignKeyConstraint(['plant_id'], ['plant.id'], ),
|
||||
sa.ForeignKeyConstraint(['update_id'], ['plant_updates.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('media')
|
||||
op.drop_table('image_hearts')
|
||||
op.drop_table('featured_images')
|
||||
op.drop_table('submission_images')
|
||||
op.drop_table('media')
|
||||
op.drop_table('plant_updates')
|
||||
op.drop_table('submissions')
|
||||
op.drop_table('transfer_request')
|
||||
op.drop_table('plant_tags')
|
||||
op.drop_table('plant_ownership_log')
|
||||
op.drop_table('plant_lineage')
|
||||
op.drop_table('grow_logs')
|
||||
op.drop_table('submission_images')
|
||||
op.drop_table('plant')
|
||||
op.drop_table('submissions')
|
||||
op.drop_table('plant_scientific_name')
|
||||
op.drop_table('users')
|
||||
op.drop_table('tag')
|
||||
op.drop_table('plant_common_name')
|
||||
op.drop_index(op.f('ix_import_batches_user_id'), table_name='import_batches')
|
||||
op.drop_table('import_batches')
|
||||
# ### end Alembic commands ###
|
38
migrations/versions/fa3de05c91fb_auto.py
Normal file
38
migrations/versions/fa3de05c91fb_auto.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: fa3de05c91fb
|
||||
Revises: cb3ce762cabb
|
||||
Create Date: 2025-06-09 10:06:16.352992
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fa3de05c91fb'
|
||||
down_revision = 'cb3ce762cabb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('plant', sa.Column('mother_uuid', sa.String(length=36), nullable=True))
|
||||
op.add_column('plant', sa.Column('notes', sa.Text(), nullable=True))
|
||||
op.add_column('plant', sa.Column('is_active', sa.Boolean(), nullable=False))
|
||||
op.add_column('plant', sa.Column('featured_media_id', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'plant', 'media', ['featured_media_id'], ['id'])
|
||||
op.create_foreign_key(None, 'plant', 'plant', ['mother_uuid'], ['uuid'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'plant', type_='foreignkey')
|
||||
op.drop_constraint(None, 'plant', type_='foreignkey')
|
||||
op.drop_column('plant', 'featured_media_id')
|
||||
op.drop_column('plant', 'is_active')
|
||||
op.drop_column('plant', 'notes')
|
||||
op.drop_column('plant', 'mother_uuid')
|
||||
# ### end Alembic commands ###
|
@ -1,32 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: fad6fe2b5e43
|
||||
Revises: 6cb1c3054071
|
||||
Create Date: 2025-06-06 09:24:38.663461
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fad6fe2b5e43'
|
||||
down_revision = '6cb1c3054071'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('submissions', 'plant_name',
|
||||
existing_type=mysql.VARCHAR(length=100),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('submissions', 'plant_name',
|
||||
existing_type=mysql.VARCHAR(length=100),
|
||||
nullable=False)
|
||||
# ### end Alembic commands ###
|
@ -1,38 +0,0 @@
|
||||
"""auto
|
||||
|
||||
Revision ID: fb0243eaa7c3
|
||||
Revises: e34cff15a95e
|
||||
Create Date: 2025-06-06 07:43:42.387700
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fb0243eaa7c3'
|
||||
down_revision = 'e34cff15a95e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('submissions', 'plant_name',
|
||||
existing_type=mysql.VARCHAR(length=100),
|
||||
nullable=False)
|
||||
op.drop_column('submissions', 'price')
|
||||
op.drop_column('submissions', 'source')
|
||||
op.drop_column('submissions', 'submission_type')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('submissions', sa.Column('submission_type', mysql.VARCHAR(length=50), nullable=False))
|
||||
op.add_column('submissions', sa.Column('source', mysql.VARCHAR(length=255), nullable=True))
|
||||
op.add_column('submissions', sa.Column('price', mysql.FLOAT(), nullable=True))
|
||||
op.alter_column('submissions', 'plant_name',
|
||||
existing_type=mysql.VARCHAR(length=100),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
@ -51,12 +51,15 @@
|
||||
</div>
|
||||
</nav>
|
||||
<main class="container">
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="alert alert-warning">
|
||||
{% for message in messages %}
|
||||
<div>{{ message }}</div>
|
||||
{% endfor %}
|
||||
<div class="container mt-3">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
@ -136,7 +136,7 @@ def upload():
|
||||
mreader = csv.DictReader(mf)
|
||||
if mreader.fieldnames != MEDIA_HEADERS:
|
||||
missing = set(MEDIA_HEADERS) - set(mreader.fieldnames or [])
|
||||
extra = set(reader.fieldnames or []) - set(MEDIA_HEADERS)
|
||||
extra = set(mreader.fieldnames or []) - set(MEDIA_HEADERS)
|
||||
os.remove(tmp_zip.name)
|
||||
flash(f"media.csv header mismatch. Missing: {missing}, Extra: {extra}", "danger")
|
||||
return redirect(request.url)
|
||||
@ -213,13 +213,13 @@ def upload():
|
||||
with open(src, "rb") as sf, open(dst, "wb") as df:
|
||||
df.write(sf.read())
|
||||
|
||||
# 🔧 FIXED: match your Media model exactly
|
||||
media = Media(
|
||||
user_id=current_user.id,
|
||||
plant_id=plant_obj.id,
|
||||
original_filename=os.path.basename(src),
|
||||
path=f"uploads/{current_user.id}/{plant_obj.id}/{fname}",
|
||||
file_url=f"uploads/{current_user.id}/{plant_obj.id}/{fname}",
|
||||
uploaded_at=datetime.fromisoformat(mrow["Uploaded At"]),
|
||||
source_type=mrow["Source Type"]
|
||||
uploader_id=current_user.id,
|
||||
caption=mrow["Source Type"],
|
||||
plant_id=plant_obj.id
|
||||
)
|
||||
db.session.add(media)
|
||||
added_media += 1
|
||||
@ -285,7 +285,7 @@ def upload():
|
||||
"mother_uuid": mother_uuid
|
||||
}
|
||||
review_list.append(item)
|
||||
session["pending_rows"].append(item)
|
||||
session["pending_rows"].append(item)
|
||||
|
||||
session["review_list"] = review_list
|
||||
return redirect(url_for("importer.review"))
|
||||
@ -336,7 +336,7 @@ def review():
|
||||
)
|
||||
db.session.add(scientific)
|
||||
db.session.flush()
|
||||
all_sci = all_scientific[scientific.name.lower()] = scientific
|
||||
all_scientific[scientific.name.lower()] = scientific
|
||||
|
||||
verified = not suggested or (suggested and accepted)
|
||||
|
||||
@ -352,6 +352,7 @@ def review():
|
||||
)
|
||||
db.session.add(plant)
|
||||
db.session.flush()
|
||||
|
||||
log = PlantOwnershipLog(
|
||||
plant_id = plant.id,
|
||||
user_id = current_user.id,
|
||||
|
@ -16,6 +16,7 @@ class Media(db.Model):
|
||||
plant_id = db.Column(db.Integer, db.ForeignKey("plant.id"), nullable=True)
|
||||
growlog_id = db.Column(db.Integer, db.ForeignKey("grow_logs.id"), nullable=True)
|
||||
update_id = db.Column(db.Integer, db.ForeignKey("plant_updates.id"), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
update = db.relationship("PlantUpdate", back_populates="media_items")
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
# plugins/media/routes.py
|
||||
|
||||
import os
|
||||
from uuid import uuid4
|
||||
from datetime import datetime
|
||||
from PIL import Image
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
redirect,
|
||||
@ -10,76 +15,139 @@ from flask import (
|
||||
current_app,
|
||||
jsonify
|
||||
)
|
||||
from flask_login import current_user, login_required
|
||||
import os
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from app import db
|
||||
from .models import Media, ImageHeart, FeaturedImage
|
||||
from plugins.plant.models import Plant
|
||||
|
||||
bp = Blueprint("media", __name__, template_folder="templates")
|
||||
bp = Blueprint("media", __name__, url_prefix="/media", template_folder="templates")
|
||||
|
||||
# We store only "YYYY/MM/DD/<uuid>.ext" in Media.file_url.
|
||||
# All files live under "/app/static/uploads/YYYY/MM/DD/<uuid>.ext" in the container.
|
||||
BASE_UPLOAD_FOLDER = "static/uploads"
|
||||
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif"}
|
||||
# -----------------------------------------------------------------------------
|
||||
# Make generate_image_url available in all templates
|
||||
# -----------------------------------------------------------------------------
|
||||
@bp.app_context_processor
|
||||
def utility_processor():
|
||||
def generate_image_url(path):
|
||||
if path:
|
||||
return url_for("media.media_file", filename=path)
|
||||
w, h = current_app.config.get("STANDARD_IMG_SIZE", (300, 200))
|
||||
return f"https://placehold.co/{w}x{h}"
|
||||
return dict(generate_image_url=generate_image_url)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Helpers & config
|
||||
# -----------------------------------------------------------------------------
|
||||
def allowed_file(filename):
|
||||
return (
|
||||
"." in filename
|
||||
and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
)
|
||||
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
|
||||
return ext in current_app.config.get("ALLOWED_EXTENSIONS", {"png","jpg","jpeg","gif"})
|
||||
|
||||
@bp.route("/media/", methods=["GET"])
|
||||
def get_upload_path():
|
||||
base = current_app.config.get("UPLOAD_FOLDER", "static/uploads")
|
||||
now = datetime.utcnow()
|
||||
subdir = os.path.join(str(now.year), f"{now.month:02}", f"{now.day:02}")
|
||||
full = os.path.join(base, subdir)
|
||||
os.makedirs(full, exist_ok=True)
|
||||
return full, subdir
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Routes
|
||||
# -----------------------------------------------------------------------------
|
||||
@bp.route("/", methods=["GET"])
|
||||
def media_index():
|
||||
"""
|
||||
/media/ is not used stand‐alone—redirect back to homepage.
|
||||
"""
|
||||
return redirect(url_for("core_ui.home"))
|
||||
|
||||
@bp.route("/media/files/<path:filename>", methods=["GET"])
|
||||
@bp.route("/files/<path:filename>", methods=["GET"])
|
||||
def media_file(filename):
|
||||
"""
|
||||
Serve files from "/app/static/uploads/<filename>".
|
||||
Example: GET /media/files/2025/06/07/abcdef1234abcd.jpg
|
||||
"""
|
||||
# Use os.getcwd() to guarantee "/app/static/uploads" (not "/app/app/static/uploads")
|
||||
full_dir = os.path.join(os.getcwd(), BASE_UPLOAD_FOLDER)
|
||||
return send_from_directory(full_dir, filename)
|
||||
# Strip leading "uploads/" if present
|
||||
if filename.startswith("uploads/"):
|
||||
filename = filename[len("uploads/"):]
|
||||
folder = current_app.config.get("UPLOAD_FOLDER", "static/uploads")
|
||||
return send_from_directory(folder, filename)
|
||||
|
||||
@bp.route("/media/heart/<int:media_id>", methods=["POST"])
|
||||
@bp.route("/heart/<int:media_id>", methods=["POST"])
|
||||
@login_required
|
||||
def toggle_heart(media_id):
|
||||
"""
|
||||
Add/remove a "heart" from an image.
|
||||
"""
|
||||
existing = ImageHeart.query.filter_by(
|
||||
user_id=current_user.id, media_id=media_id
|
||||
).first()
|
||||
|
||||
existing = ImageHeart.query.filter_by(user_id=current_user.id, media_id=media_id).first()
|
||||
if existing:
|
||||
db.session.delete(existing)
|
||||
db.session.commit()
|
||||
return jsonify({"status": "unhearted"})
|
||||
else:
|
||||
heart = ImageHeart(user_id=current_user.id, media_id=media_id)
|
||||
db.session.add(heart)
|
||||
db.session.commit()
|
||||
return jsonify({"status": "hearted"})
|
||||
heart = ImageHeart(user_id=current_user.id, media_id=media_id)
|
||||
db.session.add(heart)
|
||||
db.session.commit()
|
||||
return jsonify({"status": "hearted"})
|
||||
|
||||
@bp.route("/media/feature/<int:media_id>", methods=["POST"])
|
||||
@bp.route("/add/<string:plant_uuid>", methods=["POST"])
|
||||
@login_required
|
||||
def add_media(plant_uuid):
|
||||
plant = Plant.query.filter_by(uuid=plant_uuid).first_or_404()
|
||||
file = request.files.get("file")
|
||||
if not file or not allowed_file(file.filename):
|
||||
flash("Invalid or missing file.", "danger")
|
||||
return redirect(request.referrer or url_for("plant.edit", uuid_val=plant_uuid))
|
||||
|
||||
ext = file.filename.rsplit(".", 1)[-1].lower()
|
||||
filename = f"{uuid4()}.{ext}"
|
||||
full_path, subdir = get_upload_path()
|
||||
file.save(os.path.join(full_path, filename))
|
||||
|
||||
media = Media(
|
||||
file_url=os.path.join(subdir, filename).replace("\\", "/"),
|
||||
uploader_id=current_user.id,
|
||||
plant_id=plant.id
|
||||
)
|
||||
db.session.add(media)
|
||||
db.session.commit()
|
||||
flash("Media uploaded successfully.", "success")
|
||||
return redirect(request.referrer or url_for("plant.edit", uuid_val=plant_uuid))
|
||||
|
||||
@bp.route("/feature/<int:media_id>", methods=["POST"])
|
||||
@login_required
|
||||
def set_featured_image(media_id):
|
||||
"""
|
||||
Toggle featured status on a media item. Only the uploader or an admin may do so.
|
||||
"""
|
||||
media = Media.query.get_or_404(media_id)
|
||||
if (current_user.id != media.uploader_id) and (current_user.role != "admin"):
|
||||
if current_user.id != media.uploader_id and current_user.role != "admin":
|
||||
flash("Not authorized to set featured image.", "danger")
|
||||
return redirect(request.referrer or url_for("core_ui.home"))
|
||||
|
||||
# Remove any existing featured entries for this media
|
||||
FeaturedImage.query.filter_by(media_id=media_id).delete()
|
||||
featured = FeaturedImage(media_id=media_id, is_featured=True)
|
||||
db.session.add(featured)
|
||||
db.session.commit()
|
||||
flash("Image set as featured.", "success")
|
||||
return redirect(request.referrer or url_for("core_ui.home"))
|
||||
return redirect(request.referrer or url_for("plant.edit", uuid_val=media.plant.uuid))
|
||||
|
||||
@bp.route("/delete/<int:media_id>", methods=["POST"])
|
||||
@login_required
|
||||
def delete_media(media_id):
|
||||
media = Media.query.get_or_404(media_id)
|
||||
if current_user.id != media.uploader_id and current_user.role != "admin":
|
||||
flash("Not authorized to delete this media.", "danger")
|
||||
return redirect(request.referrer or url_for("core_ui.home"))
|
||||
|
||||
full_path = os.path.join(current_app.config.get("UPLOAD_FOLDER", "static/uploads"), media.file_url)
|
||||
if os.path.exists(full_path):
|
||||
os.remove(full_path)
|
||||
|
||||
db.session.delete(media)
|
||||
db.session.commit()
|
||||
flash("Media deleted.", "success")
|
||||
return redirect(request.referrer or url_for("plant.edit", uuid_val=media.plant.uuid))
|
||||
|
||||
@bp.route("/rotate/<int:media_id>", methods=["POST"])
|
||||
@login_required
|
||||
def rotate_media(media_id):
|
||||
media = Media.query.get_or_404(media_id)
|
||||
if current_user.id != media.uploader_id and current_user.role != "admin":
|
||||
flash("Not authorized to rotate this media.", "danger")
|
||||
return redirect(request.referrer or url_for("core_ui.home"))
|
||||
|
||||
full_path = os.path.join(current_app.config.get("UPLOAD_FOLDER", "static/uploads"), media.file_url)
|
||||
try:
|
||||
with Image.open(full_path) as img:
|
||||
img.rotate(-90, expand=True).save(full_path)
|
||||
flash("Image rotated successfully.", "success")
|
||||
except Exception as e:
|
||||
flash(f"Failed to rotate image: {e}", "danger")
|
||||
|
||||
return redirect(request.referrer or url_for("plant.edit", uuid_val=media.plant.uuid))
|
||||
|
@ -1,21 +1,38 @@
|
||||
# plugins/media/utils.py
|
||||
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from PIL import Image
|
||||
from flask import current_app, url_for
|
||||
from app import db
|
||||
from .models import Media
|
||||
from plugins.plant.models import Plant
|
||||
|
||||
|
||||
def get_upload_path():
|
||||
"""
|
||||
Return (full_disk_path, subdir) based on UTC date,
|
||||
creating directories if needed.
|
||||
e.g. ('/app/static/uploads/2025/06/09', '2025/06/09')
|
||||
"""
|
||||
base = current_app.config.get("UPLOAD_FOLDER", "static/uploads")
|
||||
now = datetime.utcnow()
|
||||
subdir = os.path.join(str(now.year), f"{now.month:02}", f"{now.day:02}")
|
||||
full = os.path.join(base, subdir)
|
||||
os.makedirs(full, exist_ok=True)
|
||||
return full, subdir
|
||||
|
||||
|
||||
def generate_random_filename(original_filename):
|
||||
"""
|
||||
Returns a random filename preserving the original extension.
|
||||
e.g. “abcd1234efgh.jpg” for “myphoto.jpg”.
|
||||
Preserve extension, randomize base name.
|
||||
"""
|
||||
ext = os.path.splitext(original_filename)[1].lower() # includes dot, e.g. ".jpg"
|
||||
random_name = uuid.uuid4().hex # 32‐char hex string
|
||||
return f"{random_name}{ext}"
|
||||
ext = os.path.splitext(original_filename)[1].lower()
|
||||
return f"{uuid.uuid4().hex}{ext}"
|
||||
|
||||
|
||||
def strip_metadata_and_save(source_file, destination_path):
|
||||
"""
|
||||
Opens an image with Pillow, strips EXIF (metadata), and saves it cleanly.
|
||||
Opens an image with Pillow, strips EXIF metadata, and saves it.
|
||||
Supports common formats (JPEG, PNG).
|
||||
"""
|
||||
with Image.open(source_file) as img:
|
||||
@ -23,3 +40,72 @@ def strip_metadata_and_save(source_file, destination_path):
|
||||
clean_image = Image.new(img.mode, img.size)
|
||||
clean_image.putdata(data)
|
||||
clean_image.save(destination_path)
|
||||
|
||||
|
||||
def generate_image_url(path):
|
||||
"""
|
||||
If path is set, route through /media/files/<path>; otherwise
|
||||
return a placehold.co URL sized to STANDARD_IMG_SIZE.
|
||||
"""
|
||||
if path:
|
||||
return url_for("media.media_file", filename=path)
|
||||
w, h = current_app.config.get("STANDARD_IMG_SIZE", (300, 200))
|
||||
return f"https://placehold.co/{w}x{h}"
|
||||
|
||||
|
||||
def save_media_file(file_storage, uploader_id, related_model=None, related_uuid=None):
|
||||
"""
|
||||
- file_storage: Werkzeug FileStorage
|
||||
- uploader_id: current_user.id
|
||||
- related_model: e.g. 'plant'
|
||||
- related_uuid: the Plant.uuid string
|
||||
Returns the new Media instance.
|
||||
"""
|
||||
full_path, subdir = get_upload_path()
|
||||
filename = generate_random_filename(file_storage.filename)
|
||||
disk_path = os.path.join(full_path, filename)
|
||||
file_storage.save(disk_path)
|
||||
|
||||
media = Media(
|
||||
file_url=os.path.join(subdir, filename).replace("\\", "/"),
|
||||
uploader_id=uploader_id
|
||||
)
|
||||
|
||||
# Associate to plant if requested
|
||||
if related_model == "plant" and related_uuid:
|
||||
plant = Plant.query.filter_by(uuid=related_uuid).first()
|
||||
if plant:
|
||||
media.plant_id = plant.id
|
||||
|
||||
db.session.add(media)
|
||||
db.session.commit()
|
||||
return media
|
||||
|
||||
|
||||
def delete_media_file(media):
|
||||
"""
|
||||
Remove file from disk and delete DB record.
|
||||
"""
|
||||
base = current_app.config.get("UPLOAD_FOLDER", "static/uploads")
|
||||
path = os.path.join(base, media.file_url)
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
pass
|
||||
db.session.delete(media)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def rotate_media_file(media, angle=-90):
|
||||
"""
|
||||
Rotate the file on disk (in place) and leave DB record intact.
|
||||
"""
|
||||
base = current_app.config.get("UPLOAD_FOLDER", "static/uploads")
|
||||
path = os.path.join(base, media.file_url)
|
||||
try:
|
||||
with Image.open(path) as img:
|
||||
rotated = img.rotate(angle, expand=True)
|
||||
rotated.save(path)
|
||||
except Exception:
|
||||
pass
|
||||
# no DB changes needed
|
||||
|
@ -1,10 +1,20 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, TextAreaField, BooleanField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
from wtforms import StringField, TextAreaField, BooleanField, SubmitField, SelectField
|
||||
from wtforms.validators import Optional, DataRequired
|
||||
|
||||
class PlantForm(FlaskForm):
|
||||
name = StringField('Name', validators=[DataRequired()])
|
||||
type = StringField('Type')
|
||||
notes = TextAreaField('Notes')
|
||||
common_name = SelectField('Common Name', validators=[Optional()], coerce=int)
|
||||
scientific_name = SelectField('Scientific Name', validators=[Optional()], coerce=int)
|
||||
mother_uuid = SelectField('Mother UUID', validators=[Optional()], coerce=str)
|
||||
plant_type = SelectField('Plant Type', validators=[DataRequired()], choices=[
|
||||
('cutting', 'Cutting'),
|
||||
('tissue_culture', 'Tissue Culture'),
|
||||
('plant', 'Plant'),
|
||||
('seed', 'Seed'),
|
||||
('division', 'Division'),
|
||||
])
|
||||
custom_slug = StringField('Custom Slug', validators=[Optional()])
|
||||
notes = TextAreaField('Notes', validators=[Optional()])
|
||||
data_verified = BooleanField('Data Verified', default=False)
|
||||
is_active = BooleanField('Active', default=True)
|
||||
submit = SubmitField('Save')
|
||||
|
@ -3,8 +3,7 @@
|
||||
from datetime import datetime
|
||||
import uuid as uuid_lib
|
||||
from app import db
|
||||
# from plugins.auth.models import User
|
||||
|
||||
from plugins.media.models import Media # import Media so we can refer to Media.plant_id
|
||||
|
||||
# Association table for Plant ↔ Tag (unchanged)
|
||||
plant_tags = db.Table(
|
||||
@ -21,7 +20,7 @@ class Tag(db.Model):
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(128), unique=True, nullable=False)
|
||||
# … any other columns you had …
|
||||
|
||||
|
||||
class PlantCommonName(db.Model):
|
||||
__tablename__ = 'plant_common_name'
|
||||
@ -38,6 +37,7 @@ class PlantCommonName(db.Model):
|
||||
cascade='all, delete-orphan'
|
||||
)
|
||||
|
||||
|
||||
class PlantScientificName(db.Model):
|
||||
__tablename__ = 'plant_scientific_name'
|
||||
__table_args__ = {'extend_existing': True}
|
||||
@ -47,22 +47,17 @@ class PlantScientificName(db.Model):
|
||||
common_id = db.Column(db.Integer, db.ForeignKey('plant_common_name.id'), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
# We removed the “plants” relationship from here to avoid backref conflicts.
|
||||
# If you need it, you can still do Plant.query.filter_by(scientific_id=<this id>).
|
||||
|
||||
class PlantOwnershipLog(db.Model):
|
||||
__tablename__ = 'plant_ownership_log'
|
||||
__table_args__ = {'extend_existing': True}
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
plant_id = db.Column(db.Integer, db.ForeignKey('plant.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
date_acquired = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
transferred = db.Column(db.Boolean, default=False, nullable=False)
|
||||
is_verified = db.Column(db.Boolean, default=False, nullable=False)
|
||||
|
||||
# Optional: if you ever want to store a pointer to the Neo4j node, you can re-add:
|
||||
# graph_node_id = db.Column(db.String(255), nullable=True)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
plant_id = db.Column(db.Integer, db.ForeignKey('plant.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
date_acquired = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
transferred = db.Column(db.Boolean, default=False, nullable=False)
|
||||
is_verified = db.Column(db.Boolean, default=False, nullable=False)
|
||||
|
||||
user = db.relationship(
|
||||
'plugins.auth.models.User',
|
||||
@ -70,56 +65,73 @@ class PlantOwnershipLog(db.Model):
|
||||
lazy=True
|
||||
)
|
||||
|
||||
|
||||
class Plant(db.Model):
|
||||
__tablename__ = 'plant'
|
||||
__table_args__ = {'extend_existing': True}
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
uuid = db.Column(db.String(36), default=lambda: str(uuid_lib.uuid4()), unique=True, nullable=False)
|
||||
custom_slug = db.Column(db.String(255), unique=True, nullable=True)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
uuid = db.Column(db.String(36), default=lambda: str(uuid_lib.uuid4()), unique=True, nullable=False)
|
||||
custom_slug = db.Column(db.String(255), unique=True, nullable=True)
|
||||
|
||||
owner_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
common_id = db.Column(db.Integer, db.ForeignKey('plant_common_name.id'), nullable=False)
|
||||
scientific_id = db.Column(db.Integer, db.ForeignKey('plant_scientific_name.id'), nullable=False)
|
||||
owner_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
common_id = db.Column(db.Integer, db.ForeignKey('plant_common_name.id'), nullable=False)
|
||||
scientific_id = db.Column(db.Integer, db.ForeignKey('plant_scientific_name.id'), nullable=False)
|
||||
|
||||
plant_type = db.Column(db.String(50), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, onupdate=datetime.utcnow)
|
||||
mother_uuid = db.Column(db.String(36), db.ForeignKey('plant.uuid'), nullable=True)
|
||||
|
||||
# ─── NEW: Flag that indicates whether the common/scientific name pair was human-verified ─────────────────
|
||||
data_verified = db.Column(db.Boolean, default=False, nullable=False)
|
||||
plant_type = db.Column(db.String(50), nullable=False)
|
||||
notes = db.Column(db.Text, nullable=True)
|
||||
is_active = db.Column(db.Boolean, default=True, nullable=False)
|
||||
featured_media_id = db.Column(db.Integer, db.ForeignKey('media.id'), nullable=True)
|
||||
|
||||
# Relationships
|
||||
updates = db.relationship(
|
||||
'plugins.growlog.models.PlantUpdate',
|
||||
backref='plant',
|
||||
lazy=True,
|
||||
cascade='all, delete-orphan'
|
||||
)
|
||||
tags = db.relationship(
|
||||
'plugins.plant.models.Tag',
|
||||
secondary=plant_tags,
|
||||
backref='plants',
|
||||
lazy='dynamic'
|
||||
)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, onupdate=datetime.utcnow)
|
||||
data_verified = db.Column(db.Boolean, default=False, nullable=False)
|
||||
|
||||
common_name = db.relationship(
|
||||
'plugins.plant.models.PlantCommonName',
|
||||
backref=db.backref('plants', lazy='dynamic'),
|
||||
lazy=True
|
||||
)
|
||||
scientific_name = db.relationship(
|
||||
'plugins.plant.models.PlantScientificName',
|
||||
backref=db.backref('plants', lazy='dynamic'),
|
||||
lazy=True
|
||||
)
|
||||
# ─── FIXED: explicitly join on Media.plant_id ──────────────────────────────
|
||||
media = db.relationship(
|
||||
Media,
|
||||
backref='plant',
|
||||
lazy=True,
|
||||
cascade='all, delete-orphan',
|
||||
foreign_keys=[Media.plant_id]
|
||||
)
|
||||
|
||||
ownership_logs = db.relationship(
|
||||
'plugins.plant.models.PlantOwnershipLog',
|
||||
backref='plant',
|
||||
lazy=True,
|
||||
cascade='all, delete-orphan'
|
||||
)
|
||||
featured_media = db.relationship(
|
||||
Media,
|
||||
foreign_keys=[featured_media_id],
|
||||
uselist=False
|
||||
)
|
||||
|
||||
updates = db.relationship(
|
||||
'plugins.growlog.models.PlantUpdate',
|
||||
backref='plant',
|
||||
lazy=True,
|
||||
cascade='all, delete-orphan'
|
||||
)
|
||||
tags = db.relationship(
|
||||
Tag,
|
||||
secondary=plant_tags,
|
||||
backref='plants',
|
||||
lazy='dynamic'
|
||||
)
|
||||
common_name = db.relationship(
|
||||
PlantCommonName,
|
||||
backref=db.backref('plants', lazy='dynamic'),
|
||||
lazy=True
|
||||
)
|
||||
scientific_name = db.relationship(
|
||||
PlantScientificName,
|
||||
backref=db.backref('plants', lazy='dynamic'),
|
||||
lazy=True
|
||||
)
|
||||
ownership_logs = db.relationship(
|
||||
PlantOwnershipLog,
|
||||
backref='plant',
|
||||
lazy=True,
|
||||
cascade='all, delete-orphan'
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Plant {self.uuid} ({self.plant_type})>"
|
||||
|
@ -1,43 +1,205 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, request, flash
|
||||
from uuid import uuid4
|
||||
from flask import (
|
||||
Blueprint,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
request,
|
||||
flash,
|
||||
)
|
||||
from flask_login import login_required, current_user
|
||||
from app import db
|
||||
from .models import Plant
|
||||
from .models import Plant, PlantCommonName, PlantScientificName
|
||||
from .forms import PlantForm
|
||||
from plugins.media.models import Media
|
||||
from plugins.media.utils import save_media_file, delete_media_file, rotate_media_file, generate_image_url
|
||||
|
||||
bp = Blueprint('plant', __name__, template_folder='templates')
|
||||
bp = Blueprint(
|
||||
'plant',
|
||||
__name__,
|
||||
url_prefix='/plants',
|
||||
template_folder='templates'
|
||||
)
|
||||
|
||||
@bp.route('/plants/')
|
||||
# -----------------------------------------------------------------------------
|
||||
# Make generate_image_url available in all templates
|
||||
# -----------------------------------------------------------------------------
|
||||
@bp.app_context_processor
|
||||
def inject_image_helper():
|
||||
return dict(generate_image_url=generate_image_url)
|
||||
|
||||
# ─── LIST ─────────────────────────────────────────────────────────────────────
|
||||
@bp.route('/', methods=['GET'])
|
||||
@login_required
|
||||
def index():
|
||||
plants = Plant.query.order_by(Plant.created_at.desc()).all()
|
||||
return render_template('plant/index.html', plants=plants)
|
||||
plants = (
|
||||
Plant.query
|
||||
.filter_by(owner_id=current_user.id)
|
||||
.order_by(Plant.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
stats = {
|
||||
'user_plants': Plant.query.filter_by(owner_id=current_user.id).count(),
|
||||
'user_images': Media.query.filter_by(uploader_id=current_user.id).count(),
|
||||
'total_plants': Plant.query.count(),
|
||||
'total_images': Media.query.count(),
|
||||
}
|
||||
return render_template('plant/index.html', plants=plants, stats=stats)
|
||||
|
||||
@bp.route('/plants/<int:plant_id>')
|
||||
def detail(plant_id):
|
||||
plant = Plant.query.get_or_404(plant_id)
|
||||
return render_template('plant/detail.html', plant=plant)
|
||||
|
||||
@bp.route('/plants/new', methods=['GET', 'POST'])
|
||||
# ─── CREATE ───────────────────────────────────────────────────────────────────
|
||||
@bp.route('/create', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def create():
|
||||
form = PlantForm()
|
||||
if form.validate_on_submit():
|
||||
plant = Plant(
|
||||
name=form.name.data,
|
||||
type=form.type.data,
|
||||
notes=form.notes.data,
|
||||
is_active=form.is_active.data
|
||||
)
|
||||
db.session.add(plant)
|
||||
db.session.commit()
|
||||
flash('Plant created successfully.', 'success')
|
||||
return redirect(url_for('plant.index'))
|
||||
return render_template('plant/form.html', form=form)
|
||||
|
||||
@bp.route('/plants/<int:plant_id>/edit', methods=['GET', 'POST'])
|
||||
def edit(plant_id):
|
||||
plant = Plant.query.get_or_404(plant_id)
|
||||
form = PlantForm(obj=plant)
|
||||
# ─── dropdown choices ───────────────────────────────────────────────────────
|
||||
form.plant_type.choices = [
|
||||
('plant', 'Plant'),
|
||||
('cutting', 'Cutting'),
|
||||
('seed', 'Seed'),
|
||||
('tissue_culture', 'Tissue Culture'),
|
||||
('division', 'Division'),
|
||||
]
|
||||
form.common_name.choices = [
|
||||
(c.id, c.name)
|
||||
for c in PlantCommonName.query.order_by(PlantCommonName.name)
|
||||
]
|
||||
form.scientific_name.choices = [
|
||||
(s.id, s.name)
|
||||
for s in PlantScientificName.query.order_by(PlantScientificName.name)
|
||||
]
|
||||
form.mother_uuid.choices = [('N/A', 'None')] + [
|
||||
(p.uuid, f"{p.common_name.name if p.common_name else 'Unnamed'} – {p.uuid}")
|
||||
for p in Plant.query.order_by(Plant.created_at.desc()).all()
|
||||
]
|
||||
|
||||
if form.validate_on_submit():
|
||||
form.populate_obj(plant)
|
||||
new_plant = Plant(
|
||||
uuid=str(uuid4()),
|
||||
owner_id=current_user.id,
|
||||
plant_type=form.plant_type.data,
|
||||
common_id=form.common_name.data,
|
||||
scientific_id=form.scientific_name.data,
|
||||
mother_uuid=(
|
||||
form.mother_uuid.data
|
||||
if form.mother_uuid.data != 'N/A'
|
||||
else None
|
||||
),
|
||||
custom_slug=form.custom_slug.data,
|
||||
notes=form.notes.data,
|
||||
data_verified=form.data_verified.data,
|
||||
is_active=form.is_active.data,
|
||||
)
|
||||
db.session.add(new_plant)
|
||||
db.session.commit()
|
||||
flash('New plant created successfully.', 'success')
|
||||
return redirect(url_for('plant.edit', uuid_val=new_plant.uuid))
|
||||
|
||||
return render_template('plant/create.html', form=form)
|
||||
|
||||
# ─── DETAIL ───────────────────────────────────────────────────────────────────
|
||||
@bp.route('/<uuid:uuid_val>', methods=['GET'])
|
||||
@login_required
|
||||
def detail(uuid_val):
|
||||
plant = Plant.query.filter_by(uuid=str(uuid_val)).first_or_404()
|
||||
return render_template('plant/detail.html', plant=plant)
|
||||
|
||||
# ─── EDIT ─────────────────────────────────────────────────────────────────────
|
||||
@bp.route('/<uuid:uuid_val>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def edit(uuid_val):
|
||||
plant = Plant.query.filter_by(uuid=str(uuid_val)).first_or_404()
|
||||
form = PlantForm()
|
||||
|
||||
form.plant_type.choices = [
|
||||
('plant', 'Plant'),
|
||||
('cutting', 'Cutting'),
|
||||
('seed', 'Seed'),
|
||||
('tissue_culture', 'Tissue Culture'),
|
||||
('division', 'Division'),
|
||||
]
|
||||
form.common_name.choices = [
|
||||
(c.id, c.name)
|
||||
for c in PlantCommonName.query.order_by(PlantCommonName.name)
|
||||
]
|
||||
form.scientific_name.choices = [
|
||||
(s.id, s.name)
|
||||
for s in PlantScientificName.query.order_by(PlantScientificName.name)
|
||||
]
|
||||
form.mother_uuid.choices = [('N/A', 'None')] + [
|
||||
(p.uuid, f"{p.common_name.name if p.common_name else 'Unnamed'} – {p.uuid}")
|
||||
for p in Plant.query.filter(Plant.uuid != plant.uuid).all()
|
||||
]
|
||||
|
||||
if request.method == 'GET':
|
||||
form.plant_type.data = plant.plant_type
|
||||
form.common_name.data = plant.common_id
|
||||
form.scientific_name.data = plant.scientific_id
|
||||
form.mother_uuid.data = plant.mother_uuid or 'N/A'
|
||||
form.custom_slug.data = plant.custom_slug
|
||||
form.notes.data = plant.notes
|
||||
form.data_verified.data = plant.data_verified
|
||||
form.is_active.data = getattr(plant, 'is_active', True)
|
||||
|
||||
if form.validate_on_submit():
|
||||
plant.plant_type = form.plant_type.data
|
||||
plant.common_id = form.common_name.data
|
||||
plant.scientific_id = form.scientific_name.data
|
||||
plant.mother_uuid = (
|
||||
form.mother_uuid.data
|
||||
if form.mother_uuid.data != 'N/A'
|
||||
else None
|
||||
)
|
||||
plant.custom_slug = form.custom_slug.data
|
||||
plant.notes = form.notes.data
|
||||
plant.data_verified = form.data_verified.data
|
||||
plant.is_active = form.is_active.data
|
||||
db.session.commit()
|
||||
flash('Plant updated successfully.', 'success')
|
||||
return redirect(url_for('plant.detail', plant_id=plant.id))
|
||||
return render_template('plant/form.html', form=form, plant=plant)
|
||||
return redirect(url_for('plant.detail', uuid_val=plant.uuid))
|
||||
|
||||
return render_template('plant/edit.html', form=form, plant=plant)
|
||||
|
||||
# ─── IMAGE ROUTES ────────────────────────────────────────────────────────────
|
||||
@bp.route('/<uuid:uuid_val>/upload', methods=['POST'])
|
||||
@login_required
|
||||
def upload_image(uuid_val):
|
||||
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
|
||||
file = request.files.get('file')
|
||||
if file and file.filename:
|
||||
save_media_file(
|
||||
file,
|
||||
current_user.id,
|
||||
related_model='plant',
|
||||
related_uuid=str(plant.uuid)
|
||||
)
|
||||
flash('Image uploaded successfully.', 'success')
|
||||
return redirect(url_for('plant.edit', uuid_val=plant.uuid))
|
||||
|
||||
@bp.route('/<uuid:uuid_val>/feature/<int:media_id>', methods=['POST'])
|
||||
@login_required
|
||||
def set_featured_image(uuid_val, media_id):
|
||||
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
|
||||
media = Media.query.get_or_404(media_id)
|
||||
plant.featured_media_id = media.id
|
||||
db.session.commit()
|
||||
flash('Featured image set.', 'success')
|
||||
return redirect(url_for('plant.edit', uuid_val=plant.uuid))
|
||||
|
||||
@bp.route('/<uuid:uuid_val>/delete/<int:media_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_image(uuid_val, media_id):
|
||||
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
|
||||
media = Media.query.get_or_404(media_id)
|
||||
delete_media_file(media)
|
||||
flash('Image deleted.', 'success')
|
||||
return redirect(url_for('plant.edit', uuid_val=plant.uuid))
|
||||
|
||||
@bp.route('/<uuid:uuid_val>/rotate/<int:media_id>', methods=['POST'])
|
||||
@login_required
|
||||
def rotate_image(uuid_val, media_id):
|
||||
plant = Plant.query.filter_by(uuid=uuid_val).first_or_404()
|
||||
media = Media.query.get_or_404(media_id)
|
||||
rotate_media_file(media)
|
||||
flash('Image rotated.', 'success')
|
||||
return redirect(url_for('plant.edit', uuid_val=plant.uuid))
|
||||
|
53
plugins/plant/templates/plant/create.html
Normal file
53
plugins/plant/templates/plant/create.html
Normal file
@ -0,0 +1,53 @@
|
||||
{% extends 'core_ui/base.html' %}
|
||||
{% block title %}Add New Plant – Nature In Pots{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<h2>Create New Plant</h2>
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.plant_type.label(class="form-label") }}
|
||||
{{ form.plant_type(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.common_name.label(class="form-label") }}
|
||||
{{ form.common_name(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.scientific_name.label(class="form-label") }}
|
||||
{{ form.scientific_name(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.mother_uuid.label(class="form-label") }}
|
||||
{{ form.mother_uuid(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.custom_slug.label(class="form-label") }}
|
||||
{{ form.custom_slug(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.notes.label(class="form-label") }}
|
||||
{{ form.notes(class="form-control", rows=4) }}
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
{{ form.data_verified(class="form-check-input") }}
|
||||
{{ form.data_verified.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
{{ form.is_active(class="form-check-input") }}
|
||||
{{ form.is_active.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">Create Plant</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,37 +1,73 @@
|
||||
{# plugins/plant/templates/plant/detail.html #}
|
||||
{% extends 'core_ui/base.html' %}
|
||||
{% block title %}{{ plant.common_name.name }} – Nature In Pots{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
{{ plant.common_name.name if plant.common_name else "Unnamed Plant" }} – Nature In Pots
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-4">
|
||||
<div class="row">
|
||||
<div class="row gx-4">
|
||||
<div class="col-md-4">
|
||||
<img src="https://placehold.co/300x300"
|
||||
class="img-fluid rounded mb-3"
|
||||
alt="{{ plant.common_name.name }}">
|
||||
{# determine featured or fallback to first media item #}
|
||||
{% set featured = plant.featured_media or plant.media|first %}
|
||||
<img
|
||||
src="{{ generate_image_url(featured.file_url if featured else None) }}"
|
||||
alt="Image of {{ plant.common_name.name if plant.common_name else 'Plant' }}"
|
||||
class="img-fluid rounded shadow-sm"
|
||||
style="object-fit: cover; width: 100%; height: auto;"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<h1>{{ plant.common_name.name }}</h1>
|
||||
<h2>
|
||||
{{ plant.common_name.name if plant.common_name else "Unnamed Plant" }}
|
||||
</h2>
|
||||
{% if plant.scientific_name %}
|
||||
<p class="text-muted"><em>{{ plant.scientific_name.name }}</em></p>
|
||||
<h5 class="text-muted">
|
||||
{{ plant.scientific_name.name }}
|
||||
</h5>
|
||||
{% endif %}
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Date Added</dt>
|
||||
<dd class="col-sm-9">{{ plant.created_at.strftime('%Y-%m-%d') }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Status</dt>
|
||||
<dd class="col-sm-9">
|
||||
{{ 'Dead' if plant.is_dead else 'Active' }}
|
||||
</dd>
|
||||
</dl>
|
||||
<p class="mt-3">
|
||||
{{ plant.notes or "No description provided." }}
|
||||
</p>
|
||||
|
||||
<a href="{{ url_for('plant.index') }}" class="btn btn-secondary">
|
||||
← Back to list
|
||||
</a>
|
||||
<a href="{{ url_for('plant.edit', plant_id=plant.id) }}"
|
||||
class="btn btn-primary">
|
||||
Edit
|
||||
</a>
|
||||
{% if plant.mother_uuid %}
|
||||
<p class="text-muted">
|
||||
Parent:
|
||||
<a href="{{ url_for('plant.detail', uuid_val=plant.mother_uuid) }}">
|
||||
{{ plant.mother_uuid }}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4">
|
||||
<a
|
||||
href="{{ url_for('plant.edit', uuid_val=plant.uuid) }}"
|
||||
class="btn btn-primary me-2"
|
||||
>Edit</a>
|
||||
<a
|
||||
href="{{ url_for('plant.index') }}"
|
||||
class="btn btn-secondary"
|
||||
>Back to List</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if plant.media|length > (1 if plant.featured_media else 0) %}
|
||||
<hr class="my-4">
|
||||
<h4>Additional Images</h4>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
{% for img in plant.media if img != featured %}
|
||||
<img
|
||||
src="{{ generate_image_url(img.file_url) }}"
|
||||
alt="Plant image"
|
||||
class="img-thumbnail"
|
||||
style="height: 160px; object-fit: cover;"
|
||||
>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
127
plugins/plant/templates/plant/edit.html
Normal file
127
plugins/plant/templates/plant/edit.html
Normal file
@ -0,0 +1,127 @@
|
||||
{% extends 'core_ui/base.html' %}
|
||||
{% block title %}Edit Plant – Nature In Pots{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<h2>Edit Plant</h2>
|
||||
|
||||
{# ─── Main plant‐data form ──────────────────────────────────────────── #}
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.plant_type.label(class="form-label") }}
|
||||
{{ form.plant_type(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.common_name.label(class="form-label") }}
|
||||
{{ form.common_name(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.scientific_name.label(class="form-label") }}
|
||||
{{ form.scientific_name(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.mother_uuid.label(class="form-label") }}
|
||||
{{ form.mother_uuid(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.custom_slug.label(class="form-label") }}
|
||||
{{ form.custom_slug(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.notes.label(class="form-label") }}
|
||||
{{ form.notes(class="form-control", rows=4) }}
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
{{ form.data_verified(class="form-check-input") }}
|
||||
{{ form.data_verified.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
{{ form.is_active(class="form-check-input") }}
|
||||
{{ form.is_active.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
{# ─── Upload new image ─────────────────────────────────────────────── #}
|
||||
<h4>Upload Image</h4>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('plant.upload_image', uuid_val=plant.uuid) }}"
|
||||
enctype="multipart/form-data"
|
||||
class="mb-4"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token() }}"
|
||||
>
|
||||
<div class="input-group">
|
||||
<input type="file" name="file" class="form-control" required>
|
||||
<button class="btn btn-secondary" type="submit">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{# ─── Existing images ──────────────────────────────────────────────── #}
|
||||
<h4>Existing Images</h4>
|
||||
<div class="row">
|
||||
{% for media in plant.media %}
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card h-100">
|
||||
<img
|
||||
src="{{ generate_image_url(media.file_url) }}"
|
||||
class="card-img-top img-fluid"
|
||||
alt="Plant Image"
|
||||
style="object-fit:cover; height:150px;"
|
||||
>
|
||||
<div class="card-body text-center">
|
||||
{% if plant.featured_media_id == media.id %}
|
||||
<span class="badge bg-success mb-2">Featured</span>
|
||||
{% else %}
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('plant.set_featured_image', uuid_val=plant.uuid, media_id=media.id) }}"
|
||||
class="mb-2"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn btn-outline-primary btn-sm">Set Featured</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<div class="d-grid gap-1">
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('plant.rotate_image', uuid_val=plant.uuid, media_id=media.id) }}"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn btn-outline-secondary btn-sm">Rotate</button>
|
||||
</form>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('plant.delete_image', uuid_val=plant.uuid, media_id=media.id) }}"
|
||||
onsubmit="return confirm('Delete this image?');"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn btn-outline-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted">No images uploaded yet.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,12 +0,0 @@
|
||||
{% extends 'core_ui/base.html' %}
|
||||
{% block content %}
|
||||
<h1>{% if plant %}Edit{% else %}New{% endif %} Plant</h1>
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
<p>{{ form.name.label }}<br>{{ form.name(size=40) }}</p>
|
||||
<p>{{ form.type.label }}<br>{{ form.type(size=40) }}</p>
|
||||
<p>{{ form.notes.label }}<br>{{ form.notes(rows=5, cols=40) }}</p>
|
||||
<p>{{ form.is_active() }} {{ form.is_active.label }}</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
</form>
|
||||
{% endblock %}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user