base_loader.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #!/usr/bin/env python3
  2. # Contest Management System - http://cms-dev.github.io/
  3. # Copyright © 2013 Giovanni Mascellani <mascellani@poisson.phc.unipi.it>
  4. # Copyright © 2014-2015 William Di Luigi <williamdiluigi@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. from abc import ABCMeta, abstractmethod
  19. class BaseLoader(metaclass=ABCMeta):
  20. """Base class for deriving loaders.
  21. Each loader must extend this class and support the following
  22. access pattern:
  23. * The class method detect() can be called at any time.
  24. """
  25. # Short name of this loader, meant to be a unique identifier.
  26. short_name = None
  27. # Description of this loader, meant to be human readable.
  28. description = None
  29. def __init__(self, path, file_cacher):
  30. """Initialize the Loader.
  31. path (str): the filesystem location given by the user.
  32. file_cacher (FileCacher): the file cacher to use to store
  33. files (i.e. statements, managers,
  34. testcases, etc.).
  35. """
  36. self.path = path
  37. self.file_cacher = file_cacher
  38. @staticmethod
  39. @abstractmethod
  40. def detect(path):
  41. """Detect whether this loader is able to interpret a path.
  42. If the loader chooses to not support autodetection, just
  43. always return False.
  44. path (string): the path to scan.
  45. return (bool): True if the loader is able to interpret the
  46. given path.
  47. """
  48. pass
  49. class TaskLoader(BaseLoader):
  50. """Base class for deriving task loaders.
  51. Each task loader must extend this class and support the following
  52. access pattern:
  53. * The class method detect() can be called at any time.
  54. * Once a loader is instatiated, get_task() can be called on it,
  55. for how many times the caller want.
  56. """
  57. def __init__(self, path, file_cacher):
  58. super().__init__(path, file_cacher)
  59. @abstractmethod
  60. def get_task(self, get_statement):
  61. """Produce a Task object.
  62. get_statement (boolean): whether the statement should be imported.
  63. return (Task): the Task object.
  64. """
  65. pass
  66. @abstractmethod
  67. def task_has_changed(self):
  68. """Detect if the task has been changed since its last import.
  69. This is expected to happen by saving, at every import, some
  70. piece of data about the last importation time. Then, when
  71. task_has_changed() is called, such time is compared with the
  72. last modification time of the files describing the task. Anyway,
  73. the TaskLoader may choose the heuristic better suited for its
  74. case.
  75. If this task is being imported for the first time or if the
  76. TaskLoader decides not to support changes detection, just return
  77. True.
  78. return (bool): True if the task was changed, False otherwise.
  79. """
  80. pass
  81. class UserLoader(BaseLoader):
  82. """Base class for deriving user loaders.
  83. Each user loader must extend this class and support the following
  84. access pattern:
  85. * The class method detect() can be called at any time.
  86. * Once a loader is instatiated, get_user() can be called on it,
  87. for how many times the caller want.
  88. """
  89. def __init__(self, path, file_cacher):
  90. super().__init__(path, file_cacher)
  91. @abstractmethod
  92. def get_user(self):
  93. """Produce a User object.
  94. return (User): the User object.
  95. """
  96. pass
  97. @abstractmethod
  98. def user_has_changed(self):
  99. """Detect if the user has been changed since its last import.
  100. This is expected to happen by saving, at every import, some
  101. piece of data about the last importation time. Then, when
  102. user_has_changed() is called, such time is compared with the
  103. last modification time of the files describing the user. Anyway,
  104. the UserLoader may choose the heuristic better suited for its
  105. case.
  106. If this user is being imported for the first time or if the
  107. UserLoader decides not to support changes detection, just return
  108. True.
  109. return (bool): True if the user was changed, False otherwise.
  110. """
  111. pass
  112. class TeamLoader(BaseLoader):
  113. """Base class for deriving team loaders.
  114. Each team loader must extend this class and support the following
  115. access pattern:
  116. * The class method detect() can be called at any time.
  117. * Once a loader is instatiated, get_team() can be called on it,
  118. for how many times the caller want.
  119. """
  120. def __init__(self, path, file_cacher):
  121. super().__init__(path, file_cacher)
  122. @abstractmethod
  123. def get_team(self):
  124. """Produce a Team object.
  125. return (Team): the Team object.
  126. """
  127. pass
  128. @abstractmethod
  129. def team_has_changed(self):
  130. """Detect if the team has been changed since its last import.
  131. This is expected to happen by saving, at every import, some
  132. piece of data about the last importation time. Then, when
  133. team_has_changed() is called, such time is compared with the
  134. last modification time of the files describing the team. Anyway,
  135. the TeamLoader may choose the heuristic better suited for its
  136. case.
  137. If this team is being imported for the first time or if the
  138. TeamLoader decides not to support changes detection, just return
  139. True.
  140. return (bool): True if the team was changed, False otherwise.
  141. """
  142. pass
  143. class ContestLoader(BaseLoader):
  144. """Base class for deriving contest loaders.
  145. Each contest loader must extend this class and support the following
  146. access pattern:
  147. * The class method detect() can be called at any time.
  148. * Once a loader is instatiated, get_contest() can be called on it,
  149. for how many times the caller want.
  150. """
  151. def __init__(self, path, file_cacher):
  152. super().__init__(path, file_cacher)
  153. @abstractmethod
  154. def get_contest(self):
  155. """Produce a Contest object.
  156. Do what is needed (i.e. search directories and explore files
  157. in the location given to the constructor) to produce a Contest
  158. object. Also get a minimal amount of information on tasks and
  159. participations, at least enough to produce a list of all task
  160. names and a list of dict objects that will represent all the
  161. participations in the contest (each participation should have
  162. at least the "username" field).
  163. return (tuple): the Contest object and the two lists described
  164. above.
  165. """
  166. pass
  167. @abstractmethod
  168. def contest_has_changed(self):
  169. """Detect if the contest has been changed since its last import.
  170. This is expected to happen by saving, at every import, some
  171. piece of data about the last importation time. Then, when
  172. contest_has_changed() is called, such time is compared with the
  173. last modification time of the files describing the contest.
  174. Anyway the ContestLoader may choose the heuristic better suited
  175. for its case.
  176. If this contest is being imported for the first time or if the
  177. ContestLoader decides not to support changes detection, just
  178. return True.
  179. return (bool): True if the contset was changed, False otherwise.
  180. """
  181. pass
  182. @abstractmethod
  183. def get_task_loader(self, taskname):
  184. """Return a loader class for the task with the given name.
  185. taskname (string): name of the task.
  186. return (TaskLoader): loader for the task with name taskname.
  187. """
  188. pass