forked from devs/PCv5
Compare commits
8 Commits
master
...
notificati
Author | SHA1 | Date |
---|---|---|
Darks | 15a4d38ea0 | |
Darks | 0c7c408e40 | |
Darks | 4868774b96 | |
Darks | f508536805 | |
Lephe | eeaab86d0a | |
Lephe | 11b19af199 | |
Darks | 201e961ba2 | |
Darks | 81c910832b |
|
@ -22,6 +22,8 @@ uwsgi.ini
|
|||
run.sh
|
||||
# Update script to pull repository from SSH
|
||||
update.sh
|
||||
# Config to set up some server specific config
|
||||
local_config.py
|
||||
|
||||
## Wiki
|
||||
|
||||
|
|
2
V5.py
2
V5.py
|
@ -1,6 +1,6 @@
|
|||
from app import app, db
|
||||
from app.models.users import User, Guest, Member, Group, GroupPrivilege
|
||||
# from app.models.models import Post
|
||||
from app.models.topic import Topic
|
||||
|
||||
|
||||
@app.shell_context_processor
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
from flask import Flask
|
||||
from flask import Flask, g
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_login import LoginManager
|
||||
from config import Config
|
||||
import time
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
db = SQLAlchemy(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
|
||||
@app.before_request
|
||||
def request_time():
|
||||
g.request_start_time = time.time()
|
||||
g.request_time = lambda: "%.5fs" % (time.time() - g.request_start_time)
|
||||
|
||||
login = LoginManager(app)
|
||||
login.login_view = 'login'
|
||||
login.login_message = "Veuillez vous authentifier avant de continuer."
|
||||
|
||||
|
||||
from app import models # IDK why this is here, but it works
|
||||
from app.models.comment import Comment
|
||||
from app.models.thread import Thread
|
||||
from app.models.forum import Forum
|
||||
from app.models.notification import Notification
|
||||
from app.routes import index, search, users # To load routes at initialization
|
||||
from app.routes.account import login, account
|
||||
from app.routes.account import login, account, notification
|
||||
from app.routes.admin import index, groups, account, trophies
|
||||
from app.utils import pluralize # To use pluralize into the templates
|
||||
from app.utils import is_title
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
shoutbox-kick shoutbox-ban
|
||||
unlimited-pms footer-statistics community-login
|
||||
access-admin-panel edit-account delete-account edit-trophies
|
||||
delete_notification
|
||||
-
|
||||
name: Modérateur
|
||||
css: "color: green"
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
from app import db
|
||||
from app.models.post import Post
|
||||
|
||||
class Comment(Post):
|
||||
__tablename__ = 'comment'
|
||||
__mapper_args__ = {'polymorphic_identity': __tablename__}
|
||||
|
||||
# ID of the associated Post object
|
||||
id = db.Column(db.Integer, db.ForeignKey('post.id'), primary_key=True)
|
||||
|
||||
# Standalone properties
|
||||
text = db.Column(db.UnicodeText)
|
||||
|
||||
# Relations
|
||||
thread_id = db.Column(db.Integer, db.ForeignKey('thread.id'),
|
||||
nullable=False)
|
||||
thread = db.relationship('Thread', backref='comments',
|
||||
foreign_keys=thread_id)
|
||||
|
||||
# attachement = db.relationship('Attachement', backref='comment')
|
||||
|
||||
def __init__(self, author, text, thread):
|
||||
Post.__init__(author, text)
|
||||
if isinstance(thread, Thread):
|
||||
thread = thread.id
|
||||
self.thread_id = thread
|
|
@ -0,0 +1,26 @@
|
|||
from app import db
|
||||
|
||||
|
||||
class Forum(db.Model):
|
||||
__tablename__ = 'forum'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
# Standalone properties
|
||||
name = db.Column(db.Unicode(64))
|
||||
slug = db.Column(db.Unicode(64))
|
||||
description = db.Column(db.UnicodeText)
|
||||
|
||||
# Relationships
|
||||
parent_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=True)
|
||||
parent = db.relationship('Forum', backref='sub_forums', remote_side=id,
|
||||
lazy=True)
|
||||
|
||||
# Also [topics] which is provided by a backref from the Topic class
|
||||
|
||||
def __init__(self, name, description, priv_prefix):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.priv_prefix = priv_prefix
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Forum: {self.name}>'
|
|
@ -0,0 +1,23 @@
|
|||
from app import db
|
||||
from datetime import datetime
|
||||
|
||||
class Notification(db.Model):
|
||||
""" A long-term `flash` notification. It is deleted when watched """
|
||||
__tablename__ = 'notification'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
text = db.Column(db.UnicodeText)
|
||||
href = db.Column(db.UnicodeText)
|
||||
date = db.Column(db.DateTime, default=datetime.now())
|
||||
|
||||
owner_id = db.Column(db.Integer, db.ForeignKey('member.id'), nullable=False)
|
||||
|
||||
def __init__(self, owner_id, text, href=None):
|
||||
""" Check weather or not the id is valid before creating the notif! """
|
||||
self.text = text
|
||||
self.href = href
|
||||
self.owner_id = owner_id
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Notification to {self.owner.name}: {self.text} ({self.href})>'
|
|
@ -1,29 +1,34 @@
|
|||
from datetime import datetime
|
||||
from app import db
|
||||
from app.models.users import *
|
||||
from app.models.users import User
|
||||
|
||||
|
||||
class Post(db.Model):
|
||||
""" Content a User can create and publish """
|
||||
__tablename__ = 'post'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
type = db.Column(db.String(20))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
'polymorphic_on': type
|
||||
}
|
||||
|
||||
# Standalone properties
|
||||
text = db.Column(db.Text(convert_unicode=True))
|
||||
date_created = db.Column(db.DateTime, default=datetime.now)
|
||||
date_modified = db.Column(db.DateTime, default=datetime.now)
|
||||
date_created = db.Column(db.DateTime)
|
||||
date_modified = db.Column(db.DateTime)
|
||||
|
||||
# Relationships
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
def __init__(self, author, text):
|
||||
""" Create a Post """
|
||||
self.text = text
|
||||
if type(author) == Member:
|
||||
if isinstance(author, User):
|
||||
author = author.id
|
||||
self.author_id = author
|
||||
self.date_created = datetime.now()
|
||||
self.date_modified = datetime.now()
|
||||
|
||||
def update(self, text):
|
||||
""" Update a post. Check whether the request sender has the right to do
|
||||
|
@ -34,7 +39,7 @@ class Post(db.Model):
|
|||
def change_ownership(self, new_author):
|
||||
""" Change ownership of a post. Check whether the request sender has the
|
||||
right to do this! """
|
||||
if type(new_author) == Member:
|
||||
if isinstance(new_author, User):
|
||||
new_author = new_author.id
|
||||
self.author_id = new_author
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class SpecialPrivilege(db.Model):
|
|||
self.priv = priv
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Privilege "{self.priv}" of member #{self.mid}>'
|
||||
return f'<Privilege: {self.priv} of member #{self.mid}>'
|
||||
|
||||
|
||||
# Group: User group, corresponds to a community role and a set of privileges
|
||||
|
@ -70,7 +70,7 @@ class Group(db.Model):
|
|||
return sorted(gp.priv for gp in gps)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Group "{self.name}">'
|
||||
return f'<Group: {self.name}>'
|
||||
|
||||
|
||||
# Many-to-many relation for users belonging to groups
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
from app import db
|
||||
from app.models.post import Post
|
||||
from app.models.comment import Comment
|
||||
from config import V5Config
|
||||
|
||||
class Thread(Post):
|
||||
""" Some thread, such as a topic, program, tutorial """
|
||||
|
||||
# Foreign Post object ID
|
||||
__tablename__ = 'thread'
|
||||
id = db.Column(db.Integer, db.ForeignKey('post.id'), primary_key=True)
|
||||
|
||||
# Identify threads as a type of posts using the table name, and add a
|
||||
# column to further discriminate types of threads
|
||||
thread_type = db.Column(db.String(20))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
'polymorphic_on': thread_type
|
||||
}
|
||||
|
||||
# Properties
|
||||
title = db.Column(db.Unicode(V5Config.THREAD_NAME_MAXLEN))
|
||||
# Also a relation [comments] populated from the Comment class.
|
||||
|
||||
# Relations
|
||||
top_comment_id = db.Column(db.Integer, db.ForeignKey('comment.id'))
|
||||
top_comment = db.relationship('Comment', foreign_keys=top_comment_id)
|
||||
|
||||
def __init__(self, author, text, title):
|
||||
""" Create a Thread """
|
||||
Post.__init__(author, text)
|
||||
self.title = title
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Thread #{self.id}'
|
|
@ -0,0 +1,22 @@
|
|||
from app import db
|
||||
from app.models.thread import Thread
|
||||
|
||||
|
||||
class Topic(Thread):
|
||||
__tablename__ = 'topic'
|
||||
id = db.Column(db.Integer, db.ForeignKey('thread.id'), primary_key=True)
|
||||
__mapper_args__ = {'polymorphic_identity': __tablename__}
|
||||
|
||||
# Relationships
|
||||
forum_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=False)
|
||||
forum = db.relationship('Forum', backref='topics',foreign_keys=forum_id)
|
||||
|
||||
def __init__(self, author, text, title, forum):
|
||||
""" Create a Topic """
|
||||
Post.__init__(author, text, title)
|
||||
if isinstance(forum, Forum):
|
||||
forum = forum.id
|
||||
self.forum_id = forum
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Topic #{self.id}'
|
|
@ -2,11 +2,12 @@ from datetime import date
|
|||
from app import db
|
||||
from flask import flash
|
||||
from flask_login import UserMixin
|
||||
from app.models.post import Post
|
||||
from app.models.privs import SpecialPrivilege, Group, GroupMember, \
|
||||
GroupPrivilege
|
||||
from app.models.trophies import Trophy, TrophyMember
|
||||
from app.models.notification import Notification
|
||||
import app.utils.unicode_names as unicode_names
|
||||
from app.utils.notify import notify
|
||||
from config import V5Config
|
||||
|
||||
import werkzeug.security
|
||||
|
@ -14,8 +15,10 @@ import re
|
|||
import math
|
||||
import app
|
||||
|
||||
# User: Website user that performs actions on the post
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
""" Website user that performs actions on the post """
|
||||
|
||||
__tablename__ = 'user'
|
||||
|
||||
# User ID, should be used to refer to any user. Thea actual user can either
|
||||
|
@ -25,7 +28,7 @@ class User(UserMixin, db.Model):
|
|||
type = db.Column(db.String(30))
|
||||
|
||||
# TODO: add good relation
|
||||
posts = db.relationship('Post', backref="author", lazy=False)
|
||||
posts = db.relationship('Post', backref="author", lazy=True)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
|
@ -35,8 +38,10 @@ class User(UserMixin, db.Model):
|
|||
def __repr__(self):
|
||||
return f'<User: #{self.id}>'
|
||||
|
||||
# Guest: Unregistered user with minimal privileges
|
||||
class Guest(User, db.Model):
|
||||
|
||||
class Guest(User):
|
||||
""" Unregistered user with minimal privileges """
|
||||
|
||||
__tablename__ = 'guest'
|
||||
__mapper_args__ = {'polymorphic_identity': __tablename__}
|
||||
|
||||
|
@ -52,8 +57,9 @@ class Guest(User, db.Model):
|
|||
return f'<Guest: {self.username} ({self.ip})>'
|
||||
|
||||
|
||||
# Member: Registered user with full access to the website's services
|
||||
class Member(User, db.Model):
|
||||
class Member(User):
|
||||
""" Registered user with full access to the website's services """
|
||||
|
||||
__tablename__ = 'member'
|
||||
__mapper_args__ = {'polymorphic_identity': __tablename__}
|
||||
|
||||
|
@ -92,6 +98,7 @@ class Member(User, db.Model):
|
|||
newsletter = db.Column(db.Boolean, default=False)
|
||||
|
||||
# Relations
|
||||
notifications = db.relationship('Notification', backref="owner", lazy=True)
|
||||
trophies = db.relationship('Trophy', secondary=TrophyMember,
|
||||
back_populates='owners')
|
||||
|
||||
|
@ -203,6 +210,14 @@ class Member(User, db.Model):
|
|||
return werkzeug.security.check_password_hash(self.password_hash,
|
||||
password)
|
||||
|
||||
def notify(self, message, href=None):
|
||||
""" Notify a user with a message.
|
||||
An hyperlink can be added to redirect to the notification source """
|
||||
|
||||
n = Notification(self.id, message, href=href)
|
||||
db.session.add(n)
|
||||
db.session.commit()
|
||||
|
||||
def add_trophy(self, t):
|
||||
"""
|
||||
Add a trophy to the current user. Check whether the request sender has
|
||||
|
@ -214,8 +229,7 @@ class Member(User, db.Model):
|
|||
t = Trophy.query.filter_by(name=t).first()
|
||||
if t not in self.trophies:
|
||||
self.trophies.append(t)
|
||||
# TODO: implement the notification system
|
||||
# self.notify(f"Vous venez de débloquer le trophée '{t.name}'")
|
||||
self.notify(f"Vous avez débloqué le trophée '{t.name}'")
|
||||
|
||||
def del_trophy(self, t):
|
||||
"""
|
||||
|
@ -225,7 +239,7 @@ class Member(User, db.Model):
|
|||
if type(t) == int:
|
||||
t = Trophy.query.get(t)
|
||||
if type(t) == str:
|
||||
t = Trophy.query.filter_by(name=name).first()
|
||||
t = Trophy.query.filter_by(name=t).first()
|
||||
if t in self.trophies:
|
||||
self.trophies.remove(t)
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
from flask import redirect, url_for, request, flash
|
||||
from flask_login import login_required, current_user
|
||||
from app import app, db
|
||||
from app.models.notification import Notification
|
||||
from app.utils.render import render
|
||||
|
||||
|
||||
@app.route('/notifications', methods=['GET'])
|
||||
@login_required
|
||||
def list_notifications():
|
||||
notifications = current_user.notifications
|
||||
return render('account/notifications.html', notifications=notifications)
|
||||
|
||||
|
||||
@app.route('/notifications/delete/<id>', methods=['GET'])
|
||||
@login_required
|
||||
def delete_notification(id=None):
|
||||
if type(id) == int:
|
||||
notification = Notification.query.get(id)
|
||||
if notification:
|
||||
# Only current user or admin can delete notifications
|
||||
if notification.owner_id == current_user.id:
|
||||
db.session.delete(notification)
|
||||
db.session.commit()
|
||||
return redirect(url_for('list_notifications'))
|
||||
elif 'delete_notification' in current_user.privs:
|
||||
db.session.delete(notification)
|
||||
db.session.commit()
|
||||
# TODO: change this redirection
|
||||
return redirect(url_for('list_notifications'))
|
||||
else:
|
||||
abort(403)
|
||||
abort(404)
|
||||
elif id == "all":
|
||||
for n in current_user.notifications:
|
||||
db.session.delete(n)
|
||||
db.session.commit()
|
||||
return redirect(url_for('list_notifications'))
|
||||
# TODO: add something to allow an admin to delete all notifs for a user
|
||||
# with a GET parameter
|
||||
else:
|
||||
abort(404)
|
|
@ -1,4 +1,5 @@
|
|||
from flask import flash, redirect, url_for
|
||||
from flask_login import current_user
|
||||
from wtforms import BooleanField
|
||||
from app.utils.priv_required import priv_required
|
||||
from app.models.users import Member
|
||||
|
@ -6,6 +7,7 @@ from app.models.trophies import Trophy
|
|||
from app.forms.account import AdminUpdateAccountForm, AdminDeleteAccountForm, \
|
||||
AdminAccountEditTrophyForm
|
||||
from app.utils.render import render
|
||||
from app.utils.notify import notify
|
||||
from app import app, db
|
||||
|
||||
|
||||
|
@ -47,6 +49,7 @@ def adm_edit_account(user_id):
|
|||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
# TODO: send an email to member saying his account has been modified
|
||||
user.notify(f"Vos informations personnelles ont été modifiées par {current_user.name}.")
|
||||
flash('Modifications effectuées', 'ok')
|
||||
else:
|
||||
flash('Erreur lors de la modification', 'error')
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<h1>Notifications</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
{% if notifications %}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Notification</th>
|
||||
<th><a href="{{ url_for('delete_notification', id='all') }}">Tout supprimer</a></th>
|
||||
</tr>
|
||||
{% for n in notifications %}
|
||||
<tr>
|
||||
<td>{{ n.date.strftime('Le %Y-%m-%d à %H:%M') }}</td>
|
||||
<td>
|
||||
{% if n.href %}<a href="{{ n.href }}">{% endif %}
|
||||
{{ n.text }}
|
||||
{% if n.href %}</a>{% endif %}
|
||||
</td>
|
||||
<td><a href="{{ url_for('delete_notification', id=n.id)}}">Supprimer</a>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
Aucune notification.
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -26,7 +26,7 @@
|
|||
<code>{{ priv }}</code>
|
||||
{{- ', ' if not loop.last }}
|
||||
{% endfor %}</td>
|
||||
<td><a href="{{ url_for('adm_edit_account', user_id=user.id) }}">Modifier</a></td>
|
||||
<td style="text-align: center"><a href="{{ url_for('adm_edit_account', user_id=user.id) }}">Modifier</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
<footer>
|
||||
<p>Planète Casio est un site communautaire non affilié à CASIO. Toute reproduction de Planète Casio, même partielle, est interdite.</p>
|
||||
<p>Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou des copyrights.</p>
|
||||
{% if current_user.is_authenticated and current_user.priv('footer-statistics') %}
|
||||
<p>Page générée en {{ g.request_time() }}</p>
|
||||
{% endif %}
|
||||
<p>Ceci est un environnement de test. Tout contenu peut être supprimé sans avertissement préalable.</p>
|
||||
</footer>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<a href="{{ url_for('user', username=current_user.name) }}">
|
||||
{{ current_user.name }}</a>
|
||||
</h2>
|
||||
<a href="#">
|
||||
<a href="{{ url_for('list_notifications') }}">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path fill="#ffffff" d="M20,2A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H6L2,22V4C2,2.89 2.9,2 4,2H20M4,4V17.17L5.17,16H20V4H4M6,7H18V9H6V7M6,11H15V13H6V11Z"></path>
|
||||
</svg>Notifications
|
||||
|
|
|
@ -7,7 +7,7 @@ def is_title(object):
|
|||
"""
|
||||
Check if an object is a title
|
||||
"""
|
||||
if type(object) == Title:
|
||||
if isinstance(object, Title):
|
||||
return "Oui"
|
||||
else:
|
||||
return "Non"
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
from app import db
|
||||
from app.models.notification import Notification
|
||||
# from app.models.users import Member
|
||||
|
||||
def notify(user, message, href=None):
|
||||
""" Notify a user (by id, name or object reference) with a message.
|
||||
An hyperlink can be added to redirect to the notification source """
|
||||
|
||||
# Cuz' duck typing is quite cool
|
||||
# TODO: maybe abort if no user is found
|
||||
if type(user) == str:
|
||||
user = Member.query.filter_by(name=user).first()
|
||||
if isinstance(user, Member):
|
||||
user = user.id
|
||||
if user and Member.query.get(user):
|
||||
n = Notification(user, message, href=href)
|
||||
db.session.add(n)
|
||||
db.session.commit()
|
||||
else:
|
||||
print("User not found")
|
|
@ -1,10 +1,10 @@
|
|||
import os
|
||||
|
||||
from local_config import DB_NAME
|
||||
|
||||
class Config(object):
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY') or 'a-random-secret-key'
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
|
||||
'postgresql+psycopg2://' + os.environ.get('USER') + ':@/pcv5'
|
||||
'postgresql+psycopg2://' + os.environ.get('USER') + ':@/' + DB_NAME
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
UPLOAD_FOLDER = './app/static/avatars'
|
||||
|
||||
|
@ -21,5 +21,5 @@ class V5Config(object):
|
|||
USER_NAME_MAXLEN = 32
|
||||
# Minimum password length for new users and new passwords
|
||||
PASSWORD_MINLEN = 10
|
||||
# Maximum topic name length
|
||||
TOPIC_NAME_MAXLEN = 32
|
||||
# Maximum thread name length
|
||||
THREAD_NAME_MAXLEN = 32
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
"""Ajout des posts
|
||||
|
||||
Revision ID: 611667e86261
|
||||
Revises: 87b039db71a5
|
||||
Create Date: 2019-08-20 11:57:56.053453
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '611667e86261'
|
||||
down_revision = '87b039db71a5'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('post',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('type', sa.String(length=20), nullable=True),
|
||||
sa.Column('text', sa.Text(_expect_unicode=True), nullable=True),
|
||||
sa.Column('date_created', sa.DateTime(), nullable=True),
|
||||
sa.Column('date_modified', sa.DateTime(), nullable=True),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.drop_table('content')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('content',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('type', sa.VARCHAR(length=20), autoincrement=False, nullable=True),
|
||||
sa.Column('data', sa.TEXT(), autoincrement=False, nullable=True),
|
||||
sa.Column('date_created', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
|
||||
sa.Column('date_modified', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
|
||||
sa.Column('author_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], name='content_author_id_fkey'),
|
||||
sa.PrimaryKeyConstraint('id', name='content_pkey')
|
||||
)
|
||||
op.drop_table('post')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,54 @@
|
|||
"""ajout des classes Comment Thread Forum
|
||||
|
||||
Revision ID: 6498631e62c5
|
||||
Revises: f3f6d7f7fa81
|
||||
Create Date: 2019-08-21 16:47:15.557948
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6498631e62c5'
|
||||
down_revision = 'f3f6d7f7fa81'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('forum',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.Unicode(length=64), nullable=True),
|
||||
sa.Column('slug', sa.Unicode(length=64), nullable=True),
|
||||
sa.Column('description', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['parent_id'], ['forum.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('thread',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('thread_type', sa.String(length=20), nullable=True),
|
||||
sa.Column('title', sa.Unicode(length=32), nullable=True),
|
||||
sa.Column('top_comment', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['id'], ['post.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('comment',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('text', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('thread_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['id'], ['post.id'], ),
|
||||
sa.ForeignKeyConstraint(['thread_id'], ['thread.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('comment')
|
||||
op.drop_table('thread')
|
||||
op.drop_table('forum')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,28 @@
|
|||
"""add-foreign-key-on-Thread
|
||||
|
||||
Revision ID: 794d44c2bef8
|
||||
Revises: 6498631e62c5
|
||||
Create Date: 2019-08-21 16:48:06.623266
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '794d44c2bef8'
|
||||
down_revision = '6498631e62c5'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_foreign_key(None, 'thread', 'comment', ['top_comment'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'thread', type_='foreignkey')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""improve relations for threads and comments
|
||||
|
||||
Revision ID: e3b140752719
|
||||
Revises: 794d44c2bef8
|
||||
Create Date: 2019-08-24 19:09:46.981771
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e3b140752719'
|
||||
down_revision = '794d44c2bef8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('thread', sa.Column('top_comment_id', sa.Integer(), nullable=True))
|
||||
op.drop_constraint('thread_top_comment_fkey', 'thread', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'thread', 'comment', ['top_comment_id'], ['id'])
|
||||
op.drop_column('thread', 'top_comment')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('thread', sa.Column('top_comment', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.drop_constraint(None, 'thread', type_='foreignkey')
|
||||
op.create_foreign_key('thread_top_comment_fkey', 'thread', 'comment', ['top_comment'], ['id'])
|
||||
op.drop_column('thread', 'top_comment_id')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,36 @@
|
|||
"""Ajout des notifications
|
||||
|
||||
Revision ID: ebca7362eb22
|
||||
Revises: e3b140752719
|
||||
Create Date: 2019-09-01 11:36:25.962212
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ebca7362eb22'
|
||||
down_revision = 'e3b140752719'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('notification',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('text', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('href', sa.UnicodeText(), nullable=True),
|
||||
sa.Column('date', sa.DateTime(), nullable=True),
|
||||
sa.Column('owner_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['owner_id'], ['member.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('notification')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,28 @@
|
|||
"""Ajout des topics/comments/autres
|
||||
|
||||
Revision ID: f3f6d7f7fa81
|
||||
Revises: 611667e86261
|
||||
Create Date: 2019-08-20 17:21:10.330435
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f3f6d7f7fa81'
|
||||
down_revision = '611667e86261'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('post', 'text')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('post', sa.Column('text', sa.TEXT(), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue