Building out enums and database admin stuff.
This commit is contained in:
parent
1a6fdfbc32
commit
89878d78ec
23
admin.py
23
admin.py
|
@ -25,13 +25,14 @@
|
||||||
|
|
||||||
from rookeries import __version__ as __app_version
|
from rookeries import __version__ as __app_version
|
||||||
from rookeries.core.config import Config
|
from rookeries.core.config import Config
|
||||||
from rookeries.core.database import init_db
|
from rookeries.core.database import Base, engine, manage_db
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
def rebuild_database(arguments):
|
def rebuild_database(arguments):
|
||||||
"""Re-synchronizes the database which is pretty useful for rapid development."""
|
"""Re-synchronizes the database which is pretty useful for rapid development."""
|
||||||
init_db(arguments.drop_tables, arguments.init_data)
|
# TODO Turn this more into a mode thing than multiple arguments.
|
||||||
|
manage_db(Base, engine, arguments.create_tables, arguments.drop_tables, arguments.init_data)
|
||||||
|
|
||||||
|
|
||||||
def print_configuration_opt(arguments):
|
def print_configuration_opt(arguments):
|
||||||
|
@ -65,11 +66,19 @@ def build_argument_parser():
|
||||||
help="Print out in a single line. Useful for environment variables.")
|
help="Print out in a single line. Useful for environment variables.")
|
||||||
config_parser.set_defaults(func=print_configuration_opt)
|
config_parser.set_defaults(func=print_configuration_opt)
|
||||||
|
|
||||||
rebuild_db_parser = sub_parser.add_parser("rebuild_db", help="Rebuilds the database.")
|
# rebuild_db_parser = sub_parser.add_parser("rebuild_db", help="Rebuilds the database.")
|
||||||
rebuild_db_parser.add_argument("--drop_tables", action="store_true", default=False,
|
# rebuild_db_parser.add_argument("--drop_tables", action="store_true", default=False,
|
||||||
help="Drops the existing tables.")
|
# help="Drops the existing tables.")
|
||||||
rebuild_db_parser.add_argument("--init_data", action="store_true", default=False, help="Initializes the database.")
|
# rebuild_db_parser.add_argument("--init_data", action="store_true", default=False, help="Initializes the database.")
|
||||||
rebuild_db_parser.set_defaults(func=rebuild_database)
|
# rebuild_db_parser.set_defaults(func=rebuild_database)
|
||||||
|
|
||||||
|
# TODO Clean this up into commands.
|
||||||
|
manage_db_parser = sub_parser.add_parser("manage_db", help="Manages the database.")
|
||||||
|
manage_db_parser.add_argument("--create_tables", action="store_true", default=False, help="Creates the tables.")
|
||||||
|
manage_db_parser.add_argument("--drop_tables", action="store_true", default=False,
|
||||||
|
help="Drops the existing tables.")
|
||||||
|
manage_db_parser.add_argument("--init_data", action="store_true", default=False, help="Initializes the database.")
|
||||||
|
manage_db_parser.set_defaults(func=rebuild_database)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Things To-Do
|
Things To-Do
|
||||||
============
|
============
|
||||||
|
|
||||||
* Build out user account management and registration. {Started}
|
* Build out user account management and registration. {In-progress}
|
||||||
* Make new logo based on APS' Mr. Penguin. {Started}
|
* Make new logo based on APS' Mr. Penguin. {Started}
|
||||||
* Build out simple to-do/project creation and management setup.
|
* Build out simple to-do/project creation and management setup.
|
||||||
* Setup a nice clutter-free deployment option for rookeri.es itself. {In-progress}
|
* Setup a nice clutter-free deployment option for rookeri.es itself. {In-progress}
|
||||||
|
@ -13,10 +13,12 @@ Things To-Do
|
||||||
* Build out a simple CMS/journal based off core database.
|
* Build out a simple CMS/journal based off core database.
|
||||||
* Fix up navigation with dynamic elements for various apps...
|
* Fix up navigation with dynamic elements for various apps...
|
||||||
|
|
||||||
* Consider using enums for some parts: https://pypi.python.org/pypi/enum/0.4.4
|
* Add in translation strings.
|
||||||
|
* Add in special JSON string responses for Flask: http://flask.pocoo.org/snippets/83/
|
||||||
|
|
||||||
Things Done
|
Things Done
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
* *2013 May 09* --- Transitioned COPYING to reStructured text.
|
* *2013 May 09* --- Transitioned COPYING to reStructured text.
|
||||||
* *2013 June 11* --- Build out setup.py and make it easy to distribute on PyPI.
|
* *2013 June 11* --- Build out setup.py and make it easy to distribute on PyPI.
|
||||||
|
* *2013 June 28* --- Using enums for some parts. See enum34 and SQLAlchemy's Enums.
|
||||||
|
|
|
@ -78,10 +78,16 @@ class Config(object):
|
||||||
web_app_config_in_json = os.environ.get(CONFIG_ENV)
|
web_app_config_in_json = os.environ.get(CONFIG_ENV)
|
||||||
if web_app_config_in_json is not None:
|
if web_app_config_in_json is not None:
|
||||||
self.load_config_from_json(web_app_config_in_json)
|
self.load_config_from_json(web_app_config_in_json)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
json_config_filename = CONFIG_FILENAME
|
json_config_filename = CONFIG_FILENAME
|
||||||
|
|
||||||
if os.path.exists(json_config_filename):
|
if os.path.exists(json_config_filename):
|
||||||
self.load_config_from_json(json_config_filename)
|
|
||||||
|
with codecs.open(json_config_filename, "rb", encoding="utf-8") as f:
|
||||||
|
json_config_str = f.read()
|
||||||
|
|
||||||
|
self.load_config_from_json(json_config_str)
|
||||||
|
|
||||||
def __dict__(self):
|
def __dict__(self):
|
||||||
"""A nicer dict representation of the configuration options."""
|
"""A nicer dict representation of the configuration options."""
|
||||||
|
|
|
@ -29,6 +29,9 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
from rookeries.core.config import Config
|
from rookeries.core.config import Config
|
||||||
|
|
||||||
|
|
||||||
|
# TODO Organize everything nicely here.
|
||||||
|
|
||||||
engine = create_engine(Config().DATABASE_URI, convert_unicode=True)
|
engine = create_engine(Config().DATABASE_URI, convert_unicode=True)
|
||||||
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
|
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
|
||||||
|
|
||||||
|
@ -36,17 +39,19 @@ Base = declarative_base()
|
||||||
Base.query = db_session.query_property()
|
Base.query = db_session.query_property()
|
||||||
|
|
||||||
|
|
||||||
# TODO Organize everything nicely here.
|
def manage_db(Base, engine, create_tables=False, drop_tables=False, populate_with_data=False):
|
||||||
|
"""Manages the database and bindings to the models. Also populates the models with initial data."""
|
||||||
def init_db(drop_tables=False, populate_with_data=False):
|
# TODO Comment.
|
||||||
"""Initializes the database and bindings to the models. Also populates the models with initial data."""
|
|
||||||
|
|
||||||
if drop_tables:
|
if drop_tables:
|
||||||
Base.metadata.drop_all(bind=engine)
|
Base.metadata.drop_all(bind=engine)
|
||||||
|
|
||||||
Base.metadata.create_all(bind=engine)
|
if create_tables:
|
||||||
|
import rookeries.core.models
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
if drop_tables and populate_with_data:
|
if drop_tables and populate_with_data:
|
||||||
# TODO Add in build out of initial models and groups.
|
# TODO Add in build out of initial models and groups.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
|
@ -23,17 +23,27 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO Add in translation string lookups...
|
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
from sqlalchemy import Column, Integer, Enum, String, DateTime, ForeignKey
|
||||||
from sqlalchemy.orm import relationship, backref
|
from sqlalchemy.orm import relationship, backref
|
||||||
from enum import Enum
|
import enum
|
||||||
|
|
||||||
from rookeries.core.database import Base
|
from rookeries.core.database import Base
|
||||||
|
|
||||||
|
|
||||||
|
# --- Statuses ---
|
||||||
|
# Considered adding enums in a manner as detailed here: http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/
|
||||||
|
# Unless someone comes up with a nicer form of enums that work nicely outside of this array, two object setup.
|
||||||
|
__POSSIBLE_USER_STATUS__ = ["Unknown", "Registered", "Active", "Disabled", "Banned"]
|
||||||
|
UserStatus = enum.Enum("UserStatus", __POSSIBLE_USER_STATUS__)
|
||||||
|
__POSSIBLE_USER_PROFILE_DETAIL_TYPES__ = ["Email", "Address", "City", "Region", "Country", "CellPhone", "Phone",
|
||||||
|
"Microblog", "Url"]
|
||||||
|
UserProfileDetailType = enum.Enum("UserProfileDetailType", __POSSIBLE_USER_PROFILE_DETAIL_TYPES__)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Models ---
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
class User(Base):
|
||||||
"""User of the Rookeries application."""
|
"""User of the Rookeries application."""
|
||||||
|
|
||||||
|
@ -47,9 +57,6 @@ class User(Base):
|
||||||
user_auth_id = Column(Integer, ForeignKey("user_authentication.id"))
|
user_auth_id = Column(Integer, ForeignKey("user_authentication.id"))
|
||||||
user_authentication = relationship("UserAuthentication", backref="user", cascade="all, delete")
|
user_authentication = relationship("UserAuthentication", backref="user", cascade="all, delete")
|
||||||
|
|
||||||
user_type_id = Column(Integer, ForeignKey('user_type.id'))
|
|
||||||
user_type = relationship("UserType", backref=backref('users', order_by=id))
|
|
||||||
|
|
||||||
def __init__(self, username=None, full_name=None, email=None):
|
def __init__(self, username=None, full_name=None, email=None):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.full_name = full_name
|
self.full_name = full_name
|
||||||
|
@ -68,59 +75,38 @@ class UserAuthentication(Base):
|
||||||
|
|
||||||
password = Column(String(512))
|
password = Column(String(512))
|
||||||
security_update_date = Column(DateTime)
|
security_update_date = Column(DateTime)
|
||||||
status = Column(String(16), ForeignKey("status.id"))
|
status = Column(Enum(__POSSIBLE_USER_STATUS__, name="user_status"))
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.status = UserStatus.Registered
|
self.status = UserStatus.Registered.name
|
||||||
self.security_update_date = datetime.now()
|
self.security_update_date = datetime.now()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<UserAuthentication('%s', '%s', '%s')>" % (self.id, self.status, self.security_update_date)
|
return "<UserAuthentication('%s', '%s', '%s')>" % (self.id, self.status, self.security_update_date)
|
||||||
|
|
||||||
|
|
||||||
# TODO Considering to add in enums in a sane manner. Looking at: http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/
|
|
||||||
|
|
||||||
class UserStatus(Enum):
|
|
||||||
"""Possible user statuses."""
|
|
||||||
|
|
||||||
__order__ = "Unknown Registered Active Disabled Banned"
|
|
||||||
Unknown = 0
|
|
||||||
Registered = 1
|
|
||||||
Active = 2
|
|
||||||
Disabled = 3
|
|
||||||
Banned = 4
|
|
||||||
|
|
||||||
|
|
||||||
class UserProfileDetailType(Enum):
|
|
||||||
"""Supports user profile details."""
|
|
||||||
|
|
||||||
# TODO Consider about wanting to limit types... and still being fairly generic
|
|
||||||
__order__ = ""
|
|
||||||
AlternativeEmail = 1
|
|
||||||
AddressStreet = 2
|
|
||||||
AddressCity = 3
|
|
||||||
AddressLocalRegion = 4
|
|
||||||
AddressRegion = 5
|
|
||||||
AddressCountry = 6
|
|
||||||
CellPhone = 7
|
|
||||||
Phone = 8
|
|
||||||
Microblog = 9
|
|
||||||
Url = 10
|
|
||||||
|
|
||||||
|
|
||||||
# TODO Add in user contact / profile info?
|
|
||||||
class UserProfileDetail(Base):
|
class UserProfileDetail(Base):
|
||||||
"""Flexible elements for storing elements of a user profile."""
|
"""Flexible elements for storing elements of a user profile."""
|
||||||
|
|
||||||
___tablename__ = "user_profile_details"
|
__tablename__ = "user_profile_details"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
|
user_id = Column(Integer, ForeignKey("user.id"))
|
||||||
|
user = relationship("User")
|
||||||
|
detail_type = Column(Enum(__POSSIBLE_USER_PROFILE_DETAIL_TYPES__, name="user_profile_detail_type"))
|
||||||
|
detail_value = Column(String(512))
|
||||||
|
|
||||||
|
# TODO Add __init__ and __repr__
|
||||||
|
|
||||||
|
|
||||||
# TODO Add more methods controlling users in groups, etc.
|
|
||||||
# TODO Add in permissions for content items, etc. rather than global admin.
|
|
||||||
class Group(Base):
|
class Group(Base):
|
||||||
|
"""Group of users that allows for controlling user interactions and permissions in a granular manner."""
|
||||||
|
|
||||||
___tablename___ = "group"
|
__tablename__ = "group"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
|
name = Column(String(512))
|
||||||
|
|
||||||
|
# TODO Add in user membership
|
||||||
|
# TODO Add in group-with-group
|
||||||
|
# TODO Add __init__ and __repr__
|
|
@ -0,0 +1,27 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2013 Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
|
#
|
||||||
|
# Rookeries is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of
|
||||||
|
# the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Rookeries is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public
|
||||||
|
# License along with Rookeries. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Please share and enjoy!
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Handles translations for the Rookeries platform.
|
||||||
|
|
||||||
|
@author: Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO Add in translation string lookups...
|
||||||
|
# TODO Look at http://docs.python.org/2/library/gettext.html
|
|
@ -24,7 +24,6 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
def deprecated(func):
|
def deprecated(func):
|
||||||
|
@ -43,16 +42,3 @@ def deprecated(func):
|
||||||
new_func.__doc__ = func.__doc__
|
new_func.__doc__ = func.__doc__
|
||||||
new_func.__dict__.update(func.__dict__)
|
new_func.__dict__.update(func.__dict__)
|
||||||
return new_func
|
return new_func
|
||||||
|
|
||||||
|
|
||||||
def create_enum_from_value(value, cls):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def create_db_enum_value_list(enum):
|
|
||||||
if not isinstance(enum, Enum):
|
|
||||||
raise TypeError("enum must be a Enum instance")
|
|
||||||
|
|
||||||
value_list = []
|
|
||||||
for key in Enum:
|
|
||||||
value_list.append(key.name)
|
|
||||||
|
|
|
@ -189,6 +189,7 @@ def register_user():
|
||||||
# TODO Make this more robust and secure...
|
# TODO Make this more robust and secure...
|
||||||
# Check if there is a closed invite restriction...
|
# Check if there is a closed invite restriction...
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|
||||||
if config.USER_REGISTRATION_INVITE_KEY is not None:
|
if config.USER_REGISTRATION_INVITE_KEY is not None:
|
||||||
invite_key = request.form["invite_key"]
|
invite_key = request.form["invite_key"]
|
||||||
if invite_key == unicode(config.USER_REGISTRATION_INVITE_KEY):
|
if invite_key == unicode(config.USER_REGISTRATION_INVITE_KEY):
|
||||||
|
@ -231,6 +232,7 @@ def display_user_profile(username=None):
|
||||||
# TODO Make some checks...
|
# TODO Make some checks...
|
||||||
user.email = request.form["email"]
|
user.email = request.form["email"]
|
||||||
user.full_name = request.form["user_full_name"]
|
user.full_name = request.form["user_full_name"]
|
||||||
|
|
||||||
db_session.commit()
|
db_session.commit()
|
||||||
return redirect(url_for("display_user_profile", username=user.username))
|
return redirect(url_for("display_user_profile", username=user.username))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue