ImportDataset.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #!/usr/bin/env python3
  2. # Contest Management System - http://cms-dev.github.io/
  3. # Copyright © 2016 William Di Luigi <williamdiluigi@gmail.com>
  4. # Copyright © 2016-2018 Stefano Maggiolo <s.maggiolo@gmail.com>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Affero General Public License as
  8. # published by the Free Software Foundation, either version 3 of the
  9. # License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Affero General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Affero General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """This script works kind of like cmsImportTask, but it assumes that the task
  19. already exists. Specifically, it will just add a new dataset (without
  20. activating it).
  21. """
  22. # We enable monkey patching to make many libraries gevent-friendly
  23. # (for instance, urllib3, used by requests)
  24. import gevent.monkey
  25. gevent.monkey.patch_all() # noqa
  26. import argparse
  27. import logging
  28. import os
  29. import sys
  30. from cms import utf8_decoder
  31. from cms.db import Dataset, SessionGen
  32. from cms.db.filecacher import FileCacher
  33. from cmscontrib.importing import ImportDataError, task_from_db
  34. from cmscontrib.loaders import choose_loader, build_epilog
  35. logger = logging.getLogger(__name__)
  36. class DatasetImporter:
  37. def __init__(self, path, description, loader_class):
  38. self.file_cacher = FileCacher()
  39. self.description = description
  40. self.loader = loader_class(os.path.abspath(path), self.file_cacher)
  41. def do_import(self):
  42. """Get the task from the TaskLoader, but store *just* its dataset."""
  43. # Get the task
  44. task = self.loader.get_task(get_statement=False)
  45. if task is None:
  46. return False
  47. # Keep the dataset (and the task name) and delete the task
  48. dataset = task.active_dataset
  49. dataset.task = None
  50. task_name = task.name
  51. del task
  52. dataset.description = self.description
  53. # Store the dataset
  54. logger.info("Creating new dataset (\"%s\") for task %s on the "
  55. "database.", dataset.description, task_name)
  56. with SessionGen() as session:
  57. try:
  58. task = task_from_db(task_name, session)
  59. self._dataset_to_db(session, dataset, task)
  60. except ImportDataError as e:
  61. logger.error(str(e))
  62. logger.info("Error while importing, no changes were made.")
  63. return False
  64. session.commit()
  65. dataset_id = dataset.id
  66. logger.info("Import finished (dataset id: %s).", dataset_id)
  67. return True
  68. @staticmethod
  69. def _dataset_to_db(session, dataset, task):
  70. old_dataset = session.query(Dataset)\
  71. .filter(Dataset.task_id == task.id)\
  72. .filter(Dataset.description == dataset.description).first()
  73. if old_dataset is not None:
  74. raise ImportDataError("Dataset \"%s\" already exists."
  75. % dataset.description)
  76. dataset.task = task
  77. session.add(dataset)
  78. return dataset
  79. def main():
  80. """Parse arguments and launch process."""
  81. parser = argparse.ArgumentParser(
  82. description="Import a new dataset for an existing task in CMS.",
  83. epilog=build_epilog(),
  84. formatter_class=argparse.RawDescriptionHelpFormatter
  85. )
  86. parser.add_argument(
  87. "-L", "--loader",
  88. action="store", type=utf8_decoder,
  89. default=None,
  90. help="use the specified loader (default: autodetect)"
  91. )
  92. parser.add_argument(
  93. "target",
  94. action="store", type=utf8_decoder,
  95. help="target file/directory from where to import the dataset"
  96. )
  97. args = parser.parse_args()
  98. args.description = input("Enter a description: ")
  99. loader_class = choose_loader(
  100. args.loader,
  101. args.target,
  102. parser.error
  103. )
  104. importer = DatasetImporter(path=args.target,
  105. description=args.description,
  106. loader_class=loader_class)
  107. success = importer.do_import()
  108. return 0 if success is True else 1
  109. if __name__ == "__main__":
  110. sys.exit(main())