Building out enums and database admin stuff.

This commit is contained in:
Dorian 2013-06-29 00:41:19 -04:00
parent 1a6fdfbc32
commit 89878d78ec
8 changed files with 97 additions and 74 deletions

View File

@ -25,13 +25,14 @@
from rookeries import __version__ as __app_version
from rookeries.core.config import Config
from rookeries.core.database import init_db
from rookeries.core.database import Base, engine, manage_db
import argparse
def rebuild_database(arguments):
"""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):
@ -65,11 +66,19 @@ def build_argument_parser():
help="Print out in a single line. Useful for environment variables.")
config_parser.set_defaults(func=print_configuration_opt)
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 = sub_parser.add_parser("rebuild_db", help="Rebuilds the database.")
# rebuild_db_parser.add_argument("--drop_tables", action="store_true", default=False,
# help="Drops the existing tables.")
# rebuild_db_parser.add_argument("--init_data", action="store_true", default=False, help="Initializes the 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.")
rebuild_db_parser.add_argument("--init_data", action="store_true", default=False, help="Initializes the database.")
rebuild_db_parser.set_defaults(func=rebuild_database)
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

View File

@ -1,7 +1,7 @@
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}
* Build out simple to-do/project creation and management setup.
* 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.
* 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
-----------
* *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 28* --- Using enums for some parts. See enum34 and SQLAlchemy's Enums.

View File

@ -78,10 +78,16 @@ class Config(object):
web_app_config_in_json = os.environ.get(CONFIG_ENV)
if web_app_config_in_json is not None:
self.load_config_from_json(web_app_config_in_json)
else:
json_config_filename = 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):
"""A nicer dict representation of the configuration options."""

View File

@ -29,6 +29,9 @@ from sqlalchemy.ext.declarative import declarative_base
from rookeries.core.config import Config
# TODO Organize everything nicely here.
engine = create_engine(Config().DATABASE_URI, convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
@ -36,17 +39,19 @@ Base = declarative_base()
Base.query = db_session.query_property()
# TODO Organize everything nicely here.
def init_db(drop_tables=False, populate_with_data=False):
"""Initializes the database and bindings to the models. Also populates the models with initial data."""
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."""
# TODO Comment.
if drop_tables:
Base.metadata.drop_all(bind=engine)
if create_tables:
import rookeries.core.models
Base.metadata.create_all(bind=engine)
if drop_tables and populate_with_data:
# TODO Add in build out of initial models and groups.
pass
pass

View File

@ -23,17 +23,27 @@
"""
# TODO Add in translation string lookups...
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 enum import Enum
import enum
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):
"""User of the Rookeries application."""
@ -47,9 +57,6 @@ class User(Base):
user_auth_id = Column(Integer, ForeignKey("user_authentication.id"))
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):
self.username = username
self.full_name = full_name
@ -68,59 +75,38 @@ class UserAuthentication(Base):
password = Column(String(512))
security_update_date = Column(DateTime)
status = Column(String(16), ForeignKey("status.id"))
status = Column(Enum(__POSSIBLE_USER_STATUS__, name="user_status"))
def __init__(self):
self.status = UserStatus.Registered
self.status = UserStatus.Registered.name
self.security_update_date = datetime.now()
def __repr__(self):
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):
"""Flexible elements for storing elements of a user profile."""
___tablename__ = "user_profile_details"
__tablename__ = "user_profile_details"
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):
"""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)
name = Column(String(512))
# TODO Add in user membership
# TODO Add in group-with-group
# TODO Add __init__ and __repr__

View File

@ -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

View File

@ -24,7 +24,6 @@
"""
import warnings
from enum import Enum
def deprecated(func):
@ -43,16 +42,3 @@ def deprecated(func):
new_func.__doc__ = func.__doc__
new_func.__dict__.update(func.__dict__)
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)

View File

@ -189,6 +189,7 @@ def register_user():
# TODO Make this more robust and secure...
# Check if there is a closed invite restriction...
config = Config()
if config.USER_REGISTRATION_INVITE_KEY is not None:
invite_key = request.form["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...
user.email = request.form["email"]
user.full_name = request.form["user_full_name"]
db_session.commit()
return redirect(url_for("display_user_profile", username=user.username))