Artberry-web/models.py

266 lines
12 KiB
Python

import os
import io
import re
import uuid
import shutil
import random
import asyncio
from datetime import datetime
import requests
from PIL import Image as PILImage
from sqlalchemy.exc import IntegrityError
from werkzeug.exceptions import BadRequest
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename
from flask import Flask, abort, render_template, redirect, url_for, request, flash, session, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_wtf import FlaskForm, RecaptchaField, CSRFProtect
from flask_wtf.file import FileAllowed
from flask_wtf.csrf import validate_csrf
from wtforms import StringField, PasswordField, SubmitField, FileField, BooleanField, RadioField, SelectField, TextAreaField
from wtforms.validators import DataRequired, Length, EqualTo, ValidationError, Regexp
from dotenv import load_dotenv, find_dotenv
import aiofiles.os
from sqlalchemy import func, or_
import magic
from config import Config
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, FileField, BooleanField
from wtforms.validators import DataRequired
from flask_wtf.file import FileAllowed
db = SQLAlchemy()
bcrypt = Bcrypt()
class Comments(db.Model):
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, nullable=False)
image_id = db.Column(db.Integer, db.ForeignKey('image.id'), nullable=True)
video_id = db.Column(db.Integer, db.ForeignKey('video.id'), nullable=True)
comic_id = db.Column(db.Integer, db.ForeignKey('comics.id'), nullable=True)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=True)
comment_text = db.Column(db.Text, nullable=False)
comment_date = db.Column(db.DateTime, default=datetime.utcnow)
image = db.relationship('Image', back_populates='comments')
video = db.relationship('Video', back_populates='comments')
comic = db.relationship('Comic', back_populates='comments', overlaps="comic_link")
post = db.relationship('Post', backref='comments')
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
encrypted_password = db.Column(db.String(60), nullable=False)
ip_address = db.Column(db.String(15), nullable=True)
avatar_file = db.Column(db.String(50), nullable=True)
banner_file = db.Column(db.String(50), nullable=True)
bio = db.Column(db.Text, nullable=True)
current_item = db.Column(db.String(30), nullable=True)
def __repr__(self):
return f'<User {self.username}>'
def check_password(self, password):
return bcrypt.check_password_hash(self.encrypted_password, password)
class Image(db.Model):
__tablename__ = 'image'
id = db.Column(db.Integer, primary_key=True)
image_file = db.Column(db.String(40), nullable=False)
username = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
publication_date = db.Column(db.DateTime, default=datetime.utcnow)
tags = db.Column(db.String(100), nullable=True)
cookie_votes = db.Column(db.Integer, default=0)
comments = db.relationship('Comments', back_populates='image', cascade='all, delete-orphan')
class Votes(db.Model):
__tablename__ = 'votes'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), nullable=False)
image_id = db.Column(db.Integer, db.ForeignKey('image.id'), nullable=False)
image = db.relationship('Image', backref=db.backref('votes', lazy=True))
def __repr__(self):
return f'<Vote {self.username} for image {self.image_id}>'
class VideoVotes(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(120), nullable=False)
video_id = db.Column(db.Integer, db.ForeignKey('video.id'), nullable=False)
class Video(db.Model):
id = db.Column(db.Integer, primary_key=True)
video_file = db.Column(db.String(100), nullable=False)
video_name = db.Column(db.String(100), nullable=False)
video_thumbnail_file = db.Column(db.String(100), nullable=False)
username = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
publication_date = db.Column(db.DateTime, default=datetime.utcnow)
tags = db.Column(db.String(100), nullable=True)
description = db.Column(db.Text, nullable=True)
cookie_votes = db.Column(db.Integer, default=0)
comments = db.relationship('Comments', back_populates='video')
class Comic(db.Model):
__tablename__ = 'comics'
id = db.Column(db.Integer, primary_key=True)
comic_folder = db.Column(db.String(100), nullable=False)
comic_thumbnail_file = db.Column(db.String(100), nullable=False)
username = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
name = db.Column(db.String(100), nullable=False, unique=True)
publication_date = db.Column(db.DateTime, default=datetime.utcnow)
tags = db.Column(db.String(100), nullable=True)
cookie_votes = db.Column(db.Integer, default=0)
comments = db.relationship('Comments', back_populates='comic', overlaps="comic_link")
pages = db.relationship('ComicPage', back_populates='comic', cascade="all, delete-orphan")
class ComicPage(db.Model):
__tablename__ = 'comic_pages'
id = db.Column(db.Integer, primary_key=True)
comic_id = db.Column(db.Integer, db.ForeignKey('comics.id', ondelete='CASCADE'), nullable=False)
page_number = db.Column(db.Integer, nullable=False)
file_path = db.Column(db.String(200), nullable=False)
comic = db.relationship('Comic', back_populates='pages')
class ComicVotes(db.Model):
__tablename__ = 'comic_votes'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), nullable=False)
comic_id = db.Column(db.Integer, db.ForeignKey('comics.id'), nullable=False)
vote = db.Column(db.Integer)
comic = db.relationship('Comic', backref='votes', lazy=True)
class Cookies(db.Model):
username = db.Column(db.String(20), primary_key=True, nullable=False)
cookies = db.Column(db.Integer, default=0)
class Views(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
image_id = db.Column(db.Integer, db.ForeignKey('image.id'), nullable=True)
video_id = db.Column(db.Integer, db.ForeignKey('video.id'), nullable=True)
username = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
view_date = db.Column(db.DateTime, default=datetime.utcnow)
__table_args__ = (
db.UniqueConstraint('image_id', 'username', name='unique_image_view'),
db.UniqueConstraint('video_id', 'username', name='unique_video_view')
)
class Item(db.Model):
__tablename__ = 'items'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
item_path = db.Column(db.String, nullable=False)
price = db.Column(db.Integer, nullable=False, default=0)
visible = db.Column(db.Boolean, default=True)
def __repr__(self):
return f'<Item {self.id}: {self.item_path}, Price: {self.price}, Visible: {self.visible}>'
class UserItem(db.Model):
__tablename__ = 'user_items'
username = db.Column(db.String, db.ForeignKey('user.username'), primary_key=True)
item_id = db.Column(db.Integer, db.ForeignKey('items.id'), primary_key=True)
item = db.relationship('Item', backref=db.backref('user_items', lazy=True))
def __repr__(self):
return f'<UserItem {self.username}, Item {self.item_id}>'
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
post_date = db.Column(db.DateTime, default=datetime.utcnow)
text = db.Column(db.Text, nullable=False)
media_file = db.Column(db.String(100), nullable=True)
user = db.relationship('User', backref=db.backref('posts', lazy=True))
class Subscription(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('subscriptions', lazy=True))
author = db.relationship('User', foreign_keys=[author_id], backref=db.backref('followers', lazy=True))
def __repr__(self):
return f'<Subscription user_id={self.user_id} author_id={self.author_id}>'
class UploadForm(FlaskForm):
image_file = FileField('Choose File', validators=[DataRequired()])
tags = StringField('Tags (comma-separated)', validators=[DataRequired()])
recaptcha = RecaptchaField()
agree_with_rules = BooleanField('I agree with the publication rules',
validators=[DataRequired(message="You must agree with the publication rules.")])
submit = SubmitField('Upload')
class UploadVideoForm(FlaskForm):
video_file = FileField('Video File', validators=[DataRequired()])
thumbnail = FileField('Thumbnail', validators=[DataRequired(), FileAllowed(['jpg', 'png', 'jpeg'])])
name = StringField('Video Name', validators=[DataRequired()])
tags = StringField('Tags', validators=[DataRequired()])
description = StringField('Description', validators=[DataRequired()])
recaptcha = RecaptchaField()
submit = SubmitField('Upload')
agree_with_rules = BooleanField('I agree with the publication rules', validators=[DataRequired()])
class UploadComicForm(FlaskForm):
title = StringField('Comic Name', validators=[DataRequired()])
thumbnail = FileField('Thumbnail', validators=[DataRequired()])
tags = StringField('Tags (comma-separated)')
recaptcha = RecaptchaField()
agree_with_rules = BooleanField('I agree with the publication rules', validators=[DataRequired(message="You must agree with the publication rules.")])
submit = SubmitField('Upload')
class EditTagsForm(FlaskForm):
tags = StringField('Tags', validators=[DataRequired(), Length(max=100)], render_kw={"placeholder": "Enter tags"})
submit = SubmitField('Save')
class EditVideoForm(FlaskForm):
video_name = StringField('Title', validators=[DataRequired(), Length(max=100)], render_kw={"placeholder": "Enter video title"})
video_thumbnail = FileField('Thumbnail', validators=[FileAllowed(['jpg', 'jpeg', 'png', 'gif'], 'Only images!')])
description = TextAreaField('Description', validators=[DataRequired(), Length(max=500)], render_kw={"placeholder": "Enter video description"})
tags = StringField('Tags', validators=[DataRequired(), Length(max=100)], render_kw={"placeholder": "Tags"})
submit = SubmitField('Save')
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
recaptcha = RecaptchaField()
submit = SubmitField('Login')
class RegistrationForm(FlaskForm):
username = StringField(
'Username',
validators=[
DataRequired(),
Length(min=3, max=20),
Regexp(r'^[a-zA-Z0-9_]+$', message="Username can contain only letters, numbers, and underscores.")
]
)
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
recaptcha = RecaptchaField()
submit = SubmitField('Register')
class EmptyForm(FlaskForm):
pass
class UpdateCookiesForm(FlaskForm):
cookies = StringField('Количество печенек', validators=[DataRequired()])
submit = SubmitField('Применить')