populate.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.utils.populate
  4. ~~~~~~~~~~~~~~~~~~~~~~
  5. A module that makes creating data more easily
  6. :copyright: (c) 2014 by the FlaskBB Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. from __future__ import unicode_literals
  10. from sqlalchemy_utils.functions import create_database
  11. from flaskbb.management.models import Setting, SettingsGroup
  12. from flaskbb.user.models import User, Group
  13. from flaskbb.forum.models import Post, Topic, Forum, Category
  14. from flaskbb.extensions import alembic, db
  15. def delete_settings_from_fixture(fixture):
  16. """Deletes the settings from a fixture from the database.
  17. Returns the deleted groups and settings.
  18. :param fixture: The fixture that should be deleted.
  19. """
  20. deleted_settings = {}
  21. for settingsgroup in fixture:
  22. group = SettingsGroup.query.filter_by(key=settingsgroup[0]).first()
  23. deleted_settings[group] = []
  24. for settings in settingsgroup[1]["settings"]:
  25. setting = Setting.query.filter_by(key=settings[0]).first()
  26. if setting:
  27. deleted_settings[group].append(setting)
  28. setting.delete()
  29. group.delete()
  30. return deleted_settings
  31. def create_settings_from_fixture(fixture):
  32. """Inserts the settings from a fixture into the database.
  33. Returns the created groups and settings.
  34. :param fixture: The fixture which should inserted.
  35. """
  36. created_settings = {}
  37. for settingsgroup in fixture:
  38. group = SettingsGroup(
  39. key=settingsgroup[0],
  40. name=settingsgroup[1]["name"],
  41. description=settingsgroup[1]["description"]
  42. )
  43. group.save()
  44. created_settings[group] = []
  45. for settings in settingsgroup[1]["settings"]:
  46. setting = Setting(
  47. key=settings[0],
  48. value=settings[1]["value"],
  49. value_type=settings[1]["value_type"],
  50. name=settings[1]["name"],
  51. description=settings[1]["description"],
  52. extra=settings[1].get("extra", ""), # Optional field
  53. settingsgroup=group.key
  54. )
  55. if setting:
  56. setting.save()
  57. created_settings[group].append(setting)
  58. return created_settings
  59. def update_settings_from_fixture(fixture, overwrite_group=False,
  60. overwrite_setting=False):
  61. """Updates the database settings from a fixture.
  62. Returns the updated groups and settings.
  63. :param fixture: The fixture which should be inserted/updated.
  64. :param overwrite_group: Set this to ``True`` if you want to overwrite
  65. the group if it already exists.
  66. Defaults to ``False``.
  67. :param overwrite_setting: Set this to ``True`` if you want to overwrite the
  68. setting if it already exists.
  69. Defaults to ``False``.
  70. """
  71. updated_settings = {}
  72. for settingsgroup in fixture:
  73. group = SettingsGroup.query.filter_by(key=settingsgroup[0]).first()
  74. if (group is not None and overwrite_group) or group is None:
  75. if group is not None:
  76. group.name = settingsgroup[1]["name"]
  77. group.description = settingsgroup[1]["description"]
  78. else:
  79. group = SettingsGroup(
  80. key=settingsgroup[0],
  81. name=settingsgroup[1]["name"],
  82. description=settingsgroup[1]["description"]
  83. )
  84. group.save()
  85. for settings in settingsgroup[1]["settings"]:
  86. setting = Setting.query.filter_by(key=settings[0]).first()
  87. if (setting is not None and overwrite_setting) or setting is None:
  88. if setting is not None:
  89. setting.value = settings[1]["value"]
  90. setting.value_type = settings[1]["value_type"]
  91. setting.name = settings[1]["name"]
  92. setting.description = settings[1]["description"]
  93. setting.extra = settings[1].get("extra", "")
  94. setting.settingsgroup = group.key
  95. else:
  96. setting = Setting(
  97. key=settings[0],
  98. value=settings[1]["value"],
  99. value_type=settings[1]["value_type"],
  100. name=settings[1]["name"],
  101. description=settings[1]["description"],
  102. extra=settings[1].get("extra", ""),
  103. settingsgroup=group.key
  104. )
  105. setting.save()
  106. updated_settings[group] = []
  107. updated_settings[group].append(setting)
  108. return updated_settings
  109. def create_default_settings():
  110. """Creates the default settings."""
  111. from flaskbb.fixtures.settings import fixture
  112. create_settings_from_fixture(fixture)
  113. def create_default_groups():
  114. """This will create the 5 default groups."""
  115. from flaskbb.fixtures.groups import fixture
  116. result = []
  117. for key, value in fixture.items():
  118. group = Group(name=key)
  119. for k, v in value.items():
  120. setattr(group, k, v)
  121. group.save()
  122. result.append(group)
  123. return result
  124. def create_user(username, password, email, groupname):
  125. """Creates a user.
  126. Returns the created user.
  127. :param username: The username of the user.
  128. :param password: The password of the user.
  129. :param email: The email address of the user.
  130. :param groupname: The name of the group to which the user
  131. should belong to.
  132. """
  133. if groupname == "member":
  134. group = Group.get_member_group()
  135. else:
  136. group = Group.query.filter(getattr(Group, groupname) == True).first()
  137. user = User.create(username=username, password=password, email=email,
  138. primary_group_id=group.id, activated=True)
  139. return user
  140. def update_user(username, password, email, groupname):
  141. """Update an existing user.
  142. Returns the updated user.
  143. :param username: The username of the user.
  144. :param password: The password of the user.
  145. :param email: The email address of the user.
  146. :param groupname: The name of the group to which the user
  147. should belong to.
  148. """
  149. user = User.query.filter_by(username=username).first()
  150. if user is None:
  151. return None
  152. if groupname == "member":
  153. group = Group.get_member_group()
  154. else:
  155. group = Group.query.filter(getattr(Group, groupname) == True).first()
  156. user.password = password
  157. user.email = email
  158. user.primary_group = group
  159. return user.save()
  160. def create_welcome_forum():
  161. """This will create the `welcome forum` with a welcome topic.
  162. Returns True if it's created successfully.
  163. """
  164. if User.query.count() < 1:
  165. return False
  166. user = User.query.filter_by(id=1).first()
  167. category = Category(title="My Category", position=1)
  168. category.save()
  169. forum = Forum(title="Welcome", description="Your first forum",
  170. category_id=category.id)
  171. forum.save()
  172. topic = Topic(title="Welcome!")
  173. post = Post(content="Have fun with your new FlaskBB Forum!")
  174. topic.save(user=user, forum=forum, post=post)
  175. return True
  176. def create_test_data(users=5, categories=2, forums=2, topics=1, posts=1):
  177. """Creates 5 users, 2 categories and 2 forums in each category.
  178. It also creates a new topic topic in each forum with a post.
  179. Returns the amount of created users, categories, forums, topics and posts
  180. as a dict.
  181. :param users: The number of users.
  182. :param categories: The number of categories.
  183. :param forums: The number of forums which are created in each category.
  184. :param topics: The number of topics which are created in each forum.
  185. :param posts: The number of posts which are created in each topic.
  186. """
  187. create_default_groups()
  188. create_default_settings()
  189. data_created = {'users': 0, 'categories': 0, 'forums': 0,
  190. 'topics': 0, 'posts': 0}
  191. # create 5 users
  192. for u in range(1, users + 1):
  193. username = "test%s" % u
  194. email = "test%s@example.org" % u
  195. user = User(username=username, password="test", email=email)
  196. user.primary_group_id = u
  197. user.activated = True
  198. user.save()
  199. data_created['users'] += 1
  200. user1 = User.query.filter_by(id=1).first()
  201. user2 = User.query.filter_by(id=2).first()
  202. # lets send them a few private messages
  203. for i in range(1, 3):
  204. # TODO
  205. pass
  206. # create 2 categories
  207. for i in range(1, categories + 1):
  208. category_title = "Test Category %s" % i
  209. category = Category(title=category_title,
  210. description="Test Description")
  211. category.save()
  212. data_created['categories'] += 1
  213. # create 2 forums in each category
  214. for j in range(1, forums + 1):
  215. if i == 2:
  216. j += 2
  217. forum_title = "Test Forum %s %s" % (j, i)
  218. forum = Forum(title=forum_title, description="Test Description",
  219. category_id=i)
  220. forum.save()
  221. data_created['forums'] += 1
  222. for t in range(1, topics + 1):
  223. # create a topic
  224. topic = Topic(title="Test Title %s" % j)
  225. post = Post(content="Test Content")
  226. topic.save(post=post, user=user1, forum=forum)
  227. data_created['topics'] += 1
  228. for p in range(1, posts + 1):
  229. # create a second post in the forum
  230. post = Post(content="Test Post")
  231. post.save(user=user2, topic=topic)
  232. data_created['posts'] += 1
  233. return data_created
  234. def insert_bulk_data(topic_count=10, post_count=100):
  235. """Creates a specified number of topics in the first forum with
  236. each topic containing a specified amount of posts.
  237. Returns the number of created topics and posts.
  238. :param topics: The amount of topics in the forum.
  239. :param posts: The number of posts in each topic.
  240. """
  241. user1 = User.query.filter_by(id=1).first()
  242. user2 = User.query.filter_by(id=2).first()
  243. forum = Forum.query.filter_by(id=1).first()
  244. last_post = Post.query.order_by(Post.id.desc()).first()
  245. last_post_id = 1 if last_post is None else last_post.id
  246. created_posts = 0
  247. created_topics = 0
  248. posts = []
  249. if not (user1 or user2 or forum):
  250. return False
  251. db.session.begin(subtransactions=True)
  252. for i in range(1, topic_count + 1):
  253. last_post_id += 1
  254. # create a topic
  255. topic = Topic(title="Test Title %s" % i)
  256. post = Post(content="First Post")
  257. topic.save(post=post, user=user1, forum=forum)
  258. created_topics += 1
  259. # create some posts in the topic
  260. for j in range(1, post_count + 1):
  261. last_post_id += 1
  262. post = Post(content="Some other Post", user=user2, topic=topic.id)
  263. topic.last_updated = post.date_created
  264. topic.post_count += 1
  265. # FIXME: Is there a way to ignore IntegrityErrors?
  266. # At the moment, the first_post_id is also the last_post_id.
  267. # This does no harm, except that in the forums view, you see
  268. # the information for the first post instead of the last one.
  269. # I run a little benchmark:
  270. # 5.3643078804 seconds to create 100 topics and 10000 posts
  271. # Using another method (where data integrity is ok) I benchmarked
  272. # these stats:
  273. # 49.7832770348 seconds to create 100 topics and 10000 posts
  274. # Uncomment the line underneath and the other line to reduce
  275. # performance but fixes the above mentioned problem.
  276. # topic.last_post_id = last_post_id
  277. created_posts += 1
  278. posts.append(post)
  279. # uncomment this and delete the one below, also uncomment the
  280. # topic.last_post_id line above. This will greatly reduce the
  281. # performance.
  282. # db.session.bulk_save_objects(posts)
  283. db.session.bulk_save_objects(posts)
  284. # and finally, lets update some stats
  285. forum.recalculate(last_post=True)
  286. user1.recalculate()
  287. user2.recalculate()
  288. return created_topics, created_posts
  289. def create_latest_db():
  290. """Creates the database including the schema using SQLAlchemy's
  291. db.create_all method instead of going through all the database revisions.
  292. The revision will be set to 'head' which indicates the latest alembic
  293. revision.
  294. """
  295. create_database(db.engine.url)
  296. db.create_all()
  297. alembic.stamp()