ImportUser.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/env python3
  2. # Contest Management System - http://cms-dev.github.io/
  3. # Copyright © 2012 Bernard Blackham <bernard@largestprime.net>
  4. # Copyright © 2010-2011 Giovanni Mascellani <mascellani@poisson.phc.unipi.it>
  5. # Copyright © 2010-2018 Stefano Maggiolo <s.maggiolo@gmail.com>
  6. # Copyright © 2010-2011 Matteo Boscariol <boscarim@hotmail.com>
  7. # Copyright © 2014-2015 William Di Luigi <williamdiluigi@gmail.com>
  8. # Copyright © 2015 Luca Chiodini <luca@chiodini.org>
  9. #
  10. # This program is free software: you can redistribute it and/or modify
  11. # it under the terms of the GNU Affero General Public License as
  12. # published by the Free Software Foundation, either version 3 of the
  13. # License, or (at your option) any later version.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU Affero General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU Affero General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. """This script imports a user from disk using one of the available
  23. loaders.
  24. The data parsed by the loader is used to create a new User in the
  25. database.
  26. """
  27. # We enable monkey patching to make many libraries gevent-friendly
  28. # (for instance, urllib3, used by requests)
  29. import gevent.monkey
  30. gevent.monkey.patch_all() # noqa
  31. import argparse
  32. import logging
  33. import os
  34. import sys
  35. from cms import utf8_decoder
  36. from cms.db import Participation, SessionGen, User
  37. from cms.db.filecacher import FileCacher
  38. from cmscontrib.importing import ImportDataError, contest_from_db
  39. from cmscontrib.loaders import choose_loader, build_epilog
  40. logger = logging.getLogger(__name__)
  41. class UserImporter:
  42. """This script creates a user
  43. """
  44. def __init__(self, path, contest_id, loader_class):
  45. self.file_cacher = FileCacher()
  46. self.contest_id = contest_id
  47. self.loader = loader_class(os.path.abspath(path), self.file_cacher)
  48. def do_import(self):
  49. """Get the user from the UserLoader and store it."""
  50. # Get the user
  51. user = self.loader.get_user()
  52. if user is None:
  53. return False
  54. # Store
  55. logger.info("Creating user %s on the database.", user.username)
  56. with SessionGen() as session:
  57. try:
  58. contest = contest_from_db(self.contest_id, session)
  59. user = self._user_to_db(session, user)
  60. except ImportDataError as e:
  61. logger.error(str(e))
  62. logger.info("Error while importing, no changes were made.")
  63. return False
  64. if contest is not None:
  65. logger.info("Creating participation of user %s in contest %s.",
  66. user.username, contest.name)
  67. session.add(Participation(user=user, contest=contest))
  68. session.commit()
  69. user_id = user.id
  70. logger.info("Import finished (new user id: %s).", user_id)
  71. return True
  72. def do_import_all(self, base_path, get_loader):
  73. """Get the participation list from the ContestLoader and then
  74. try to import the corresponding users.
  75. """
  76. _, _, participations = self.loader.get_contest()
  77. for p in participations:
  78. user_path = os.path.join(base_path, p["username"])
  79. importer = UserImporter(
  80. path=user_path,
  81. contest_id=self.contest_id,
  82. loader_class=get_loader(user_path)
  83. )
  84. importer.do_import()
  85. return True
  86. @staticmethod
  87. def _user_to_db(session, user):
  88. """Add the user to the DB
  89. Return the user again, or raise in case a user with the same username
  90. was already present in the DB.
  91. """
  92. old_user = session.query(User)\
  93. .filter(User.username == user.username).first()
  94. if old_user is not None:
  95. raise ImportDataError(
  96. "User \"%s\" already exists." % user.username)
  97. session.add(user)
  98. return user
  99. def main():
  100. """Parse arguments and launch process.
  101. """
  102. parser = argparse.ArgumentParser(
  103. description="Import a user to the database.",
  104. epilog=build_epilog(),
  105. formatter_class=argparse.RawDescriptionHelpFormatter
  106. )
  107. parser.add_argument(
  108. "-L", "--loader",
  109. action="store", type=utf8_decoder,
  110. default=None,
  111. help="use the specified loader (default: autodetect)"
  112. )
  113. parser.add_argument(
  114. "target",
  115. action="store", type=utf8_decoder, nargs="?",
  116. default=os.getcwd(),
  117. help="target file/directory from where to import user(s)"
  118. )
  119. parser.add_argument(
  120. "-A", "--all",
  121. action="store_true",
  122. help="try to import all users inside target"
  123. )
  124. parser.add_argument(
  125. "-c", "--contest-id",
  126. action="store", type=int,
  127. help="id of the contest the users will be attached to"
  128. )
  129. args = parser.parse_args()
  130. def get_loader(path):
  131. return choose_loader(args.loader, path, parser.error)
  132. importer = UserImporter(
  133. path=args.target,
  134. contest_id=args.contest_id,
  135. loader_class=get_loader(args.target)
  136. )
  137. if args.all:
  138. success = importer.do_import_all(args.target, get_loader)
  139. else:
  140. success = importer.do_import()
  141. return 0 if success is True else 1
  142. if __name__ == "__main__":
  143. sys.exit(main())