populate.py 13 KB

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