forked from cms-dev/cms
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Write the machinery required to make Importer and Reimporter fully mo…
…dular. Now there can be many loader, selectable at run time, with also the possibility of autodetection.
- Loading branch information
Showing
5 changed files
with
150 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
# -*- coding: utf-8 -*- | ||
|
||
# Programming contest management system | ||
# Copyright © 2010-2012 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2013 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2012 Stefano Maggiolo <[email protected]> | ||
# Copyright © 2010-2012 Matteo Boscariol <[email protected]> | ||
# Copyright © 2013 Luca Wehrstedt <[email protected]> | ||
|
@@ -20,9 +20,10 @@ | |
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
"""This script imports a contest from disk using YamlLoader. | ||
"""This script imports a contest from disk using one of the available | ||
loaders. | ||
The data parsed by YamlLoader is used to create a new Contest in the | ||
The data parsed by the loader is used to create a new Contest in the | ||
database. | ||
""" | ||
|
@@ -36,30 +37,31 @@ | |
|
||
from cms import logger | ||
from cms.db.FileCacher import FileCacher | ||
from cms.db.SQLAlchemyAll import metadata, SessionGen, FSObject, User, \ | ||
from cms.db.SQLAlchemyAll import metadata, SessionGen, User, \ | ||
drop_everything | ||
|
||
from cmscontrib.YamlLoader import YamlLoader | ||
from cmscontrib.Loaders import choose_loader, build_epilog | ||
|
||
|
||
class Importer: | ||
|
||
"""This script imports a contest from disk using YamlLoader. | ||
"""This script imports a contest from disk using one of the | ||
available loaders. | ||
The data parsed by YamlLoader is used to create a new Contest in | ||
The data parsed by the loader is used to create a new Contest in | ||
the database. | ||
""" | ||
|
||
def __init__(self, path, drop, test, zero_time, user_number): | ||
def __init__(self, path, drop, test, zero_time, user_number, loader_class): | ||
self.drop = drop | ||
self.test = test | ||
self.zero_time = zero_time | ||
self.user_number = user_number | ||
|
||
self.file_cacher = FileCacher() | ||
|
||
self.loader = YamlLoader(os.path.realpath(path), self.file_cacher) | ||
self.loader = loader_class(os.path.realpath(path), self.file_cacher) | ||
|
||
def _prepare_db(self): | ||
logger.info("Creating database structure.") | ||
|
@@ -123,29 +125,38 @@ def do_import(self): | |
|
||
def main(): | ||
"""Parse arguments and launch process.""" | ||
|
||
parser = argparse.ArgumentParser( | ||
description="Import a contest from disk using YamlLoader") | ||
description="Import a contest from disk", | ||
epilog=build_epilog(), | ||
formatter_class=argparse.RawDescriptionHelpFormatter) | ||
group = parser.add_mutually_exclusive_group() | ||
group.add_argument("-z", "--zero-time", action="store_true", | ||
help="set to zero contest start and stop time") | ||
group.add_argument("-t", "--test", action="store_true", | ||
help="setup a contest for testing " | ||
"(times: 0, 2*10^9; ips: unset, passwords: a)") | ||
"(times: 1970, 2100; ips: unset, passwords: a)") | ||
parser.add_argument("-d", "--drop", action="store_true", | ||
help="drop everything from the database " | ||
"before importing") | ||
parser.add_argument("-n", "--user-number", action="store", type=int, | ||
help="put N random users instead of importing them") | ||
parser.add_argument("-L", "--loader", action="store", default=None, | ||
help="use the specified loader (default: autodetect)") | ||
parser.add_argument("import_directory", | ||
help="source directory from where import") | ||
|
||
args = parser.parse_args() | ||
loader_class = choose_loader(args.loader, | ||
args.import_directory, | ||
parser.error) | ||
|
||
Importer(path=args.import_directory, | ||
drop=args.drop, | ||
test=args.test, | ||
zero_time=args.zero_time, | ||
user_number=args.user_number).do_import() | ||
user_number=args.user_number, | ||
loader_class=loader_class).do_import() | ||
|
||
|
||
if __name__ == "__main__": | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#!/usr/bin/env python2 | ||
# -*- coding: utf-8 -*- | ||
|
||
# Programming contest management system | ||
# Copyright © 2013 Giovanni Mascellani <[email protected]> | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
"""List of loaders known by CMS, with some support functions. | ||
""" | ||
|
||
from cmscontrib.YamlLoader import YamlLoader | ||
|
||
LOADERS = dict((loader_class.short_name(), loader_class) | ||
for loader_class in [YamlLoader]) | ||
|
||
|
||
def choose_loader(arg, path, error_callback): | ||
"""Decide which loader to use, depending upon the specified | ||
argument and possibly performing an autodetection. | ||
The autodetection is done by calling detect() on all the known | ||
loaders and returning the only one that returns True. If no one or | ||
more than one return True, then the autodetection is considered | ||
failed and None is returned. | ||
arg (string): the argument, possibly None, passed to the program | ||
as loader specification. | ||
path (string): the path passed to the program from which to | ||
perform the loading. | ||
error_callback (method): a method to call to report errors. | ||
return (class): the chosen loader class. | ||
""" | ||
if arg is not None: | ||
try: | ||
return LOADERS[arg] | ||
except KeyError: | ||
error_callback("Specified loader doesn't exist") | ||
else: | ||
res = None | ||
for loader in LOADERS.itervalues(): | ||
if loader.detect(path): | ||
if res is None: | ||
res = loader | ||
else: | ||
error_callback( | ||
"Couldn't autodetect the loader, " | ||
"please specify it: more than one " | ||
"loader accepted the detection") | ||
if res is None: | ||
error_callback("Couldn't autodetect the loader, " | ||
"please specify it: no " | ||
"loader accepted the detection") | ||
return res | ||
|
||
|
||
def build_epilog(): | ||
epilog = "The following loaders are supported:\n" | ||
for short_name, loader_class in sorted(LOADERS.items()): | ||
epilog += " * %s (%s)\n" % (short_name, loader_class.description()) | ||
return epilog |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
# -*- coding: utf-8 -*- | ||
|
||
# Programming contest management system | ||
# Copyright © 2010-2012 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2013 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2012 Stefano Maggiolo <[email protected]> | ||
# Copyright © 2010-2012 Matteo Boscariol <[email protected]> | ||
# Copyright © 2013 Luca Versari <[email protected]> | ||
|
@@ -21,9 +21,10 @@ | |
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
"""This script reimports a contest from disk using YamlLoader. | ||
"""This script reimports a contest from disk using one of the | ||
available loaders. | ||
The data parsed by YamlLoader is used to update a Contest that's | ||
The data parsed by the loader is used to update a Contest that's | ||
already existing in the database. | ||
""" | ||
|
@@ -38,7 +39,7 @@ | |
from cms.db.SQLAlchemyAll import \ | ||
SessionGen, Base, Contest, User, Task, Submission | ||
|
||
from cmscontrib.YamlLoader import YamlLoader | ||
from cmscontrib.Loaders import choose_loader, build_epilog | ||
|
||
|
||
def _is_rel(prp, attr): | ||
|
@@ -48,20 +49,21 @@ def _is_rel(prp, attr): | |
|
||
class Reimporter: | ||
|
||
"""This script reimports a contest from disk using YamlLoader | ||
"""This script reimports a contest from disk using the specified | ||
loader. | ||
The data parsed by YamlLoader is used to update a Contest that's | ||
The data parsed by the loader is used to update a Contest that's | ||
already existing in the database. | ||
""" | ||
|
||
def __init__(self, path, contest_id, force): | ||
def __init__(self, path, contest_id, force, loader_class): | ||
self.old_contest_id = contest_id | ||
self.force = force | ||
|
||
self.file_cacher = FileCacher() | ||
|
||
self.loader = YamlLoader(os.path.realpath(path), self.file_cacher) | ||
self.loader = loader_class(os.path.realpath(path), self.file_cacher) | ||
|
||
def _update_columns(self, old_object, new_object): | ||
for prp in old_object._col_props: | ||
|
@@ -285,23 +287,31 @@ def do_reimport(self): | |
def main(): | ||
"""Parse arguments and launch process.""" | ||
parser = argparse.ArgumentParser( | ||
description="Reimport a contest from disk using YamlLoader") | ||
description="Reimport a contest from disk", | ||
epilog=build_epilog(), | ||
formatter_class=argparse.RawDescriptionHelpFormatter) | ||
parser.add_argument("-c", "--contest-id", action="store", type=int, | ||
help="id of contest to overwrite") | ||
parser.add_argument("-f", "--force", action="store_true", | ||
help="force the reimport even if some users or tasks " | ||
"may get lost") | ||
parser.add_argument("-L", "--loader", action="store", default=None, | ||
help="use the specified loader (default: autodetect)") | ||
parser.add_argument("import_directory", | ||
help="source directory from where import") | ||
|
||
args = parser.parse_args() | ||
loader_class = choose_loader(args.loader, | ||
args.import_directory, | ||
parser.error) | ||
|
||
if args.contest_id is None: | ||
args.contest_id = ask_for_contest() | ||
|
||
Reimporter(path=args.import_directory, | ||
contest_id=args.contest_id, | ||
force=args.force).do_reimport() | ||
force=args.force, | ||
loader_class=loader_class).do_reimport() | ||
|
||
|
||
if __name__ == "__main__": | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
# -*- coding: utf-8 -*- | ||
|
||
# Programming contest management system | ||
# Copyright © 2010-2012 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2013 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2012 Stefano Maggiolo <[email protected]> | ||
# Copyright © 2010-2012 Matteo Boscariol <[email protected]> | ||
# Copyright © 2013 Luca Wehrstedt <[email protected]> | ||
|
@@ -72,6 +72,34 @@ def __init__(self, path, file_cacher): | |
self.path = path | ||
self.file_cacher = file_cacher | ||
|
||
@classmethod | ||
def short_name(cls): | ||
"""Short name of this loader. | ||
""" | ||
return 'italy_yaml' | ||
|
||
@classmethod | ||
def description(cls): | ||
"""Description of this loader. | ||
""" | ||
return 'Italian YAML-based format' | ||
|
||
@classmethod | ||
def detect(cls, path): | ||
"""Detect whether this loader is able to interpret the given | ||
path. | ||
path (string): the path to scan. | ||
return (bool): True if the loader is able to interpret the | ||
given path. | ||
""" | ||
# Not really refined... | ||
return os.path.exists(os.path.join(path, "contest.yaml")) | ||
|
||
def get_contest(self): | ||
|
||
"""Produce a Contest object. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
# -*- coding: utf-8 -*- | ||
|
||
# Programming contest management system | ||
# Copyright © 2010-2012 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2013 Giovanni Mascellani <[email protected]> | ||
# Copyright © 2010-2013 Stefano Maggiolo <[email protected]> | ||
# Copyright © 2010-2012 Matteo Boscariol <[email protected]> | ||
# | ||
|
@@ -125,8 +125,8 @@ def do_setup(): | |
"cmsRemoveUser=cmscontrib.RemoveUser:main", | ||
"cmsRemoveTask=cmscontrib.RemoveTask:main", | ||
"cmsComputeComplexity=cmscontrib.ComputeComplexity:main", | ||
"cmsYamlImporter=cmscontrib.YamlImporter:main", | ||
"cmsYamlReimporter=cmscontrib.YamlReimporter:main", | ||
"cmsImporter=cmscontrib.Importer:main", | ||
"cmsReimporter=cmscontrib.Reimporter:main", | ||
"cmsSpoolExporter=cmscontrib.SpoolExporter:main", | ||
"cmsContestExporter=cmscontrib.ContestExporter:main", | ||
"cmsContestImporter=cmscontrib.ContestImporter:main", | ||
|