From ec5645f051f253e3514fc10104bb1dfcedbef503 Mon Sep 17 00:00:00 2001 From: mheyen Date: Wed, 23 Jun 2021 12:07:12 +0200 Subject: [PATCH 01/10] Initial commit for script to create sample data on an Opencast system --- create_oc_testdata/README.md | 41 ++++++++++++++++++ create_oc_testdata/config.py | 16 +++++++ create_oc_testdata/main.py | 73 ++++++++++++++++++++++++++++++++ create_oc_testdata/parse_args.py | 26 ++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 create_oc_testdata/README.md create mode 100644 create_oc_testdata/config.py create mode 100644 create_oc_testdata/main.py create mode 100644 create_oc_testdata/parse_args.py diff --git a/create_oc_testdata/README.md b/create_oc_testdata/README.md new file mode 100644 index 0000000..96890e9 --- /dev/null +++ b/create_oc_testdata/README.md @@ -0,0 +1,41 @@ +# Script for populating an Opencast system with test data + +This script creates random samples of Events (and Series) on the specified Opencast system. + + +## How to Use + +### Configuration +The script is configured by editing the values in `config.py`: + +| Configuration Key | Description | Default/Example | +| :---------------- | :---------------------------------------- | :--------------------------- | +| `target_url` | The server URL of the target tenant | https://tenant2.opencast.com | +| `digest_user` | The user name of the digest user | opencast_system_account | +| `digest_pw` | The password of the digest user | CHANGE_ME | +| `workflow_id` | The id of the workflow to start on ingest | reimport-workflow | +| `workflow_config` | The configuration for that workflow | {"autopublish": "false"} | + + +The configured digest user needs to exist on the specified Opencast system. + +### Usage + +The script can be called with the following parameters (all parameters in brackets are optional): + +`main.py [-t TARGET_URL ] [-n NUMBER_OF_EVENTS ]` + +| Short Option | Long Option | Description | +| :----------: | :---------- | :-------------------------------------------------------------- | +| `-t` | `--target` | The target url of the Opencast system | +| `-n` | `--number` | The number of Events to be created | + +#### Usage example + +`main.py -t localhost:8080 -n 10` + +## Requirements + +This script was written for Python 3.8. + +It uses modules contained in the _lib_ directory. diff --git a/create_oc_testdata/config.py b/create_oc_testdata/config.py new file mode 100644 index 0000000..62eb63d --- /dev/null +++ b/create_oc_testdata/config.py @@ -0,0 +1,16 @@ +# Configuration + +# target url for the Opencast system +# "https://tenant1.opencast.com" +target_url = "http://localhost:8080" + +# default value for the number of events to be created +number_of_events = 2 + +# digest login +digest_user = "opencast_system_account" +digest_pw = "CHANGE_ME" + +# workflow config +workflow_id = "reimport-workflow" +workflow_config = {"autopublish": "false"} diff --git a/create_oc_testdata/main.py b/create_oc_testdata/main.py new file mode 100644 index 0000000..a569148 --- /dev/null +++ b/create_oc_testdata/main.py @@ -0,0 +1,73 @@ +import os +import sys + +sys.path.append(os.path.join(os.path.abspath('..'), "lib")) + +import config +# from data_handling.elements import get_id +# from data_handling.errors import MediaPackageError +# from data_handling.parse_manifest import parse_manifest_from_endpoint +# from import_mp.import_mp import import_mp +# from input_output.input import get_yes_no_answer +# from rest_requests.api_requests import get_events_of_series +# from rest_requests.assetmanager_requests import get_media_package +from rest_requests.request import get_request, post_request +from rest_requests.request_error import RequestError +from parse_args import parse_args +from args.digest_login import DigestLogin + + +def main(): + """ + Populate the specified Opencast system with random sample events + """ + + # use api/events/{id} + + target_url, number_of_events = parse_args() + digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) + + if not target_url: + target_url = config.target_url + if not number_of_events: + number_of_events = config.number_of_events + + print(number_of_events) + print(target_url) + + print("Starting population process.") + + # test request + url = f'{target_url}/api/info/me' + response = get_request(url, digest_login, "events") + print(response.json()) + + for i in range(number_of_events): + try: + # create event + url = f'{target_url}/api/info/me' + + data = { + + } + response = post_request(url, digest_login, "events", data=data) + print(response.json()) + + except Exception as e: + print("ERROR") + print(str(e)) + + print("Done.") + + +def __abort_script(message): + print(message) + sys.exit() + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print("\nAborting process.") + sys.exit(0) diff --git a/create_oc_testdata/parse_args.py b/create_oc_testdata/parse_args.py new file mode 100644 index 0000000..9c906c1 --- /dev/null +++ b/create_oc_testdata/parse_args.py @@ -0,0 +1,26 @@ +from args.args_parser import get_args_parser +from args.args_error import args_error + + +def parse_args(): + """ + Parse the arguments and check them for correctness + + :return: target_url and number of events to be created if given + :rtype: str, int + """ + parser, optional_args, required_args = get_args_parser() + + optional_args.add_argument("-t", "--target_url", type=str, nargs='+', help="URL of target system") + optional_args.add_argument("-n", "--number", type=int, nargs='+', help="number of events") + + args = parser.parse_args() + + if not args.target_url: + args.target_url = [None] + if not args.number: + args.number = [None] + elif args.number[0] > 1000: + args_error(parser, "to many events ...") + + return args.target_url[0], args.number[0] From 87a59db62531fc6c1ecc0a25f8820ec9b20f5f05 Mon Sep 17 00:00:00 2001 From: mheyen Date: Thu, 24 Jun 2021 13:47:30 +0200 Subject: [PATCH 02/10] The script can now generate an ingest request to create an event. Basic request to ingest a test video. the creator and title of the event are now randomly generated. more parameters should be added and randomly generated. --- create_oc_testdata/config.py | 3 +++ create_oc_testdata/main.py | 51 +++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/create_oc_testdata/config.py b/create_oc_testdata/config.py index 62eb63d..b2c2cd4 100644 --- a/create_oc_testdata/config.py +++ b/create_oc_testdata/config.py @@ -4,6 +4,9 @@ # "https://tenant1.opencast.com" target_url = "http://localhost:8080" +# path to a test video +test_video_path = '/home/malte/IdeaProjects/helper-scripts/create_oc_testdata/video_test.mp4' + # default value for the number of events to be created number_of_events = 2 diff --git a/create_oc_testdata/main.py b/create_oc_testdata/main.py index a569148..1c56c0a 100644 --- a/create_oc_testdata/main.py +++ b/create_oc_testdata/main.py @@ -4,17 +4,19 @@ sys.path.append(os.path.join(os.path.abspath('..'), "lib")) import config -# from data_handling.elements import get_id -# from data_handling.errors import MediaPackageError -# from data_handling.parse_manifest import parse_manifest_from_endpoint -# from import_mp.import_mp import import_mp -# from input_output.input import get_yes_no_answer -# from rest_requests.api_requests import get_events_of_series -# from rest_requests.assetmanager_requests import get_media_package -from rest_requests.request import get_request, post_request +from rest_requests.request import get_request, post_request, big_post_request from rest_requests.request_error import RequestError from parse_args import parse_args from args.digest_login import DigestLogin +import string +import random + + +alphabet = list(string.ascii_letters) +for i in range(10): + alphabet.append(str(i)) +for c in ['Ä','Ö','Ü','ä','ö','ü']: + alphabet.append(c) def main(): @@ -22,7 +24,7 @@ def main(): Populate the specified Opencast system with random sample events """ - # use api/events/{id} + # use api/events/{id} ? target_url, number_of_events = parse_args() digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) @@ -39,25 +41,42 @@ def main(): # test request url = f'{target_url}/api/info/me' - response = get_request(url, digest_login, "events") - print(response.json()) + try: + response = get_request(url, digest_login, "events") + except: + __abort_script("Something went wrong. No Connection to API. Stopping script. ") for i in range(number_of_events): try: # create event - url = f'{target_url}/api/info/me' + url = f'{target_url}/ingest/addMediaPackage/fast' + files = [config.test_video_path] data = { - + 'creator': __generate_random_name(), + 'title': __generate_random_name(), + 'flavor': 'presentation/source', + 'acl': '{"acl": {"ace": [{"allow": true,"role": "ROLE_Z","action": "dance"}]}}' } - response = post_request(url, digest_login, "events", data=data) - print(response.json()) + + response = big_post_request(url, digest_login, "events", data=data, files=files) + print(response) except Exception as e: print("ERROR") print(str(e)) + __abort_script("Something went wrong. Could not create event. Stopping script") + + print("Done.") + + +def __generate_random_name(): + + name = '' + for i in range(random.randint(1, 30)): + name += random.choice(alphabet) - print("Done.") + return name def __abort_script(message): From 2919d038e826fac77cdd2e6ad989cfded6e82532 Mon Sep 17 00:00:00 2001 From: mheyen Date: Wed, 30 Jun 2021 11:34:05 +0200 Subject: [PATCH 03/10] Added optional -f/file argument which allows to provide a path to a test video when calling the script --- create_oc_testdata/parse_args.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/create_oc_testdata/parse_args.py b/create_oc_testdata/parse_args.py index 9c906c1..9629c68 100644 --- a/create_oc_testdata/parse_args.py +++ b/create_oc_testdata/parse_args.py @@ -2,6 +2,9 @@ from args.args_error import args_error +MAX_NUMBER_OF_EVENTS = 1000 + + def parse_args(): """ Parse the arguments and check them for correctness @@ -13,6 +16,7 @@ def parse_args(): optional_args.add_argument("-t", "--target_url", type=str, nargs='+', help="URL of target system") optional_args.add_argument("-n", "--number", type=int, nargs='+', help="number of events") + optional_args.add_argument("-f", "--file", type=str, nargs='+', help="path to test file") args = parser.parse_args() @@ -20,7 +24,9 @@ def parse_args(): args.target_url = [None] if not args.number: args.number = [None] - elif args.number[0] > 1000: + elif args.number[0] > MAX_NUMBER_OF_EVENTS: args_error(parser, "to many events ...") + if not args.file: + args.file = [None] - return args.target_url[0], args.number[0] + return args.target_url[0], args.number[0], args.file[0] From 34e8033e4bcd0ce668470ca95a94c3138b9f0750 Mon Sep 17 00:00:00 2001 From: mheyen Date: Wed, 30 Jun 2021 11:35:53 +0200 Subject: [PATCH 04/10] Updated README --- create_oc_testdata/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/create_oc_testdata/README.md b/create_oc_testdata/README.md index 96890e9..35dcd22 100644 --- a/create_oc_testdata/README.md +++ b/create_oc_testdata/README.md @@ -2,10 +2,10 @@ This script creates random samples of Events (and Series) on the specified Opencast system. - ## How to Use ### Configuration + The script is configured by editing the values in `config.py`: | Configuration Key | Description | Default/Example | @@ -25,14 +25,15 @@ The script can be called with the following parameters (all parameters in bracke `main.py [-t TARGET_URL ] [-n NUMBER_OF_EVENTS ]` -| Short Option | Long Option | Description | -| :----------: | :---------- | :-------------------------------------------------------------- | -| `-t` | `--target` | The target url of the Opencast system | -| `-n` | `--number` | The number of Events to be created | +| Short Option | Long Option | Description | +| :----------: | :---------- | :----------------------------------------- | +| `-t` | `--target` | The target url of the Opencast system | +| `-n` | `--number` | The number of Events to be created | +| `-f` | `--file` | The path to a test video | #### Usage example -`main.py -t localhost:8080 -n 10` +`main.py -t localhost:8080 -n 10 -f home/test/video.mp3` ## Requirements From 4ebb74d913b24051640f681f0e8fa51fe059be31 Mon Sep 17 00:00:00 2001 From: mheyen Date: Wed, 30 Jun 2021 11:36:44 +0200 Subject: [PATCH 05/10] Series are now being created and more meta data was added to the events. The script can now be configured to create a specified number of Series. The events will be randomly added to certain Series. More meta data (location, series, and more acl details) are added to each event. Changed the testing video file in the configurations. --- create_oc_testdata/config.py | 16 +++---- create_oc_testdata/main.py | 89 ++++++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 33 deletions(-) diff --git a/create_oc_testdata/config.py b/create_oc_testdata/config.py index b2c2cd4..067e36e 100644 --- a/create_oc_testdata/config.py +++ b/create_oc_testdata/config.py @@ -1,19 +1,19 @@ # Configuration -# target url for the Opencast system -# "https://tenant1.opencast.com" +# default target url for the Opencast system target_url = "http://localhost:8080" -# path to a test video -test_video_path = '/home/malte/IdeaProjects/helper-scripts/create_oc_testdata/video_test.mp4' +# default path to a test video +test_video_path = '/home/malte/IdeaProjects/helper-scripts/create_oc_testdata/test_video.mkv' # default value for the number of events to be created -number_of_events = 2 +number_of_events = 6 +# default value for the number of series to be created +number_of_series = 2 +# default value for the number of copies per event to be created +number_of_copies = 2 # digest login digest_user = "opencast_system_account" digest_pw = "CHANGE_ME" -# workflow config -workflow_id = "reimport-workflow" -workflow_config = {"autopublish": "false"} diff --git a/create_oc_testdata/main.py b/create_oc_testdata/main.py index 1c56c0a..0c0427c 100644 --- a/create_oc_testdata/main.py +++ b/create_oc_testdata/main.py @@ -12,53 +12,81 @@ import random -alphabet = list(string.ascii_letters) -for i in range(10): - alphabet.append(str(i)) -for c in ['Ä','Ö','Ü','ä','ö','ü']: - alphabet.append(c) - - def main(): """ Populate the specified Opencast system with random sample events """ - # use api/events/{id} ? - - target_url, number_of_events = parse_args() + target_url, number_of_events, file_path = parse_args() digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) if not target_url: target_url = config.target_url if not number_of_events: number_of_events = config.number_of_events + if not file_path: + file_path = config.test_video_path print(number_of_events) print(target_url) print("Starting population process.") - # test request + # test API connection url = f'{target_url}/api/info/me' try: - response = get_request(url, digest_login, "events") + get_request(url, digest_login, "events") except: __abort_script("Something went wrong. No Connection to API. Stopping script. ") - for i in range(number_of_events): + # Serien erstellen + series = [] + + for i in range(config.number_of_series): + + url = f'{target_url}/series/' + id = __generate_random_name() + series.append(id) + data = { + 'identifier': id, + 'publisher': __generate_random_name(), + 'title': __generate_random_name(), + 'acl': '{"acl": {"ace": [' + '{"allow": true,"role": "ROLE_ANONYMOUS","action": "write"}, ' + '{"allow": true,"role": "ROLE_ADMIN","action": "read"}' + ']}}' + } + try: - # create event - url = f'{target_url}/ingest/addMediaPackage/fast' - files = [config.test_video_path] + response = post_request(url, digest_login, "series", data=data) + print(response) - data = { - 'creator': __generate_random_name(), - 'title': __generate_random_name(), - 'flavor': 'presentation/source', - 'acl': '{"acl": {"ace": [{"allow": true,"role": "ROLE_Z","action": "dance"}]}}' - } + except Exception as e: + print("ERROR") + print(str(e)) + __abort_script("Something went wrong. Could not create series. Stopping script") + # Events erstellen + for i in range(number_of_events): + + url = f'{target_url}/ingest/addMediaPackage' # schedule-and-upload + files = [file_path] + + data = { + 'creator': __generate_random_name(), + 'title': __generate_random_name(), + 'flavor': 'presentation/source', + 'description': 'This is a test description. This Event is only for testing purposes. ', + 'spatial': __generate_random_name(), + 'isPartOf': random.choice(series), + 'acl': '{"acl": {"ace": [' + '{"allow": true,"role": "ROLE_ADMIN","action": "read"}, ' + '{"allow": true,"role": "ROLE_ADMIN","action": "write"}, ' + '{"allow": true,"role": "ROLE_ANONYMOUS","action": "read"}' + ']}}' + } + + try: response = big_post_request(url, digest_login, "events", data=data, files=files) print(response) @@ -67,15 +95,26 @@ def main(): print(str(e)) __abort_script("Something went wrong. Could not create event. Stopping script") - print("Done.") + # ToDo copy the Events + print("Done.") -def __generate_random_name(): +def __create_alphabet(): + alphabet = list(string.ascii_letters) + for i in range(10): + alphabet.append(str(i)) + for c in ['Ä','Ö','Ü','ä','ö','ü']: + alphabet.append(c) + return alphabet + +alphabet = __create_alphabet() + + +def __generate_random_name(): name = '' for i in range(random.randint(1, 30)): name += random.choice(alphabet) - return name From e04706035cc56f72a2a156076df642379f534de9 Mon Sep 17 00:00:00 2001 From: mheyen Date: Wed, 30 Jun 2021 11:40:05 +0200 Subject: [PATCH 06/10] Added new testing video --- create_oc_testdata/test_video.mkv | Bin 0 -> 192455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 create_oc_testdata/test_video.mkv diff --git a/create_oc_testdata/test_video.mkv b/create_oc_testdata/test_video.mkv new file mode 100644 index 0000000000000000000000000000000000000000..bd404860329255cb66645da073eae6ab401a24c9 GIT binary patch literal 192455 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 VrhhK1_-N4KijU0i47;Vr{TIRL+#vt} literal 0 HcmV?d00001 From 994f2772f12114f716665c17bc019f3143a6deaf Mon Sep 17 00:00:00 2001 From: mheyen Date: Mon, 5 Jul 2021 20:32:47 +0200 Subject: [PATCH 07/10] Split up the code into one event generation and one duplication script --- create_oc_testdata/{ => data}/test_video.mkv | Bin .../{ => duplicate_events}/README.md | 0 create_oc_testdata/duplicate_events/config.py | 14 ++++ create_oc_testdata/duplicate_events/main.py | 73 ++++++++++++++++++ .../duplicate_events/parse_args.py | 32 ++++++++ create_oc_testdata/generate_events/README.md | 42 ++++++++++ .../{ => generate_events}/config.py | 9 +-- .../{ => generate_events}/main.py | 71 ++++++++--------- .../{ => generate_events}/parse_args.py | 2 +- 9 files changed, 199 insertions(+), 44 deletions(-) rename create_oc_testdata/{ => data}/test_video.mkv (100%) rename create_oc_testdata/{ => duplicate_events}/README.md (100%) create mode 100644 create_oc_testdata/duplicate_events/config.py create mode 100644 create_oc_testdata/duplicate_events/main.py create mode 100644 create_oc_testdata/duplicate_events/parse_args.py create mode 100644 create_oc_testdata/generate_events/README.md rename create_oc_testdata/{ => generate_events}/config.py (62%) rename create_oc_testdata/{ => generate_events}/main.py (63%) rename create_oc_testdata/{ => generate_events}/parse_args.py (97%) diff --git a/create_oc_testdata/test_video.mkv b/create_oc_testdata/data/test_video.mkv similarity index 100% rename from create_oc_testdata/test_video.mkv rename to create_oc_testdata/data/test_video.mkv diff --git a/create_oc_testdata/README.md b/create_oc_testdata/duplicate_events/README.md similarity index 100% rename from create_oc_testdata/README.md rename to create_oc_testdata/duplicate_events/README.md diff --git a/create_oc_testdata/duplicate_events/config.py b/create_oc_testdata/duplicate_events/config.py new file mode 100644 index 0000000..7f8a0eb --- /dev/null +++ b/create_oc_testdata/duplicate_events/config.py @@ -0,0 +1,14 @@ +# Configuration + +# default target url for the Opencast system +target_url = "http://localhost:8080" + +# default path to the .yaml file containing the event IDs +yaml_file_path = '../create_oc_testdata/data/event_ids.yaml' + +# default value for the number of copies per event to be created +number_of_duplicates = 2 + +# digest login +digest_user = "opencast_system_account" +digest_pw = "CHANGE_ME" diff --git a/create_oc_testdata/duplicate_events/main.py b/create_oc_testdata/duplicate_events/main.py new file mode 100644 index 0000000..55b408c --- /dev/null +++ b/create_oc_testdata/duplicate_events/main.py @@ -0,0 +1,73 @@ +import os +import sys + +sys.path.append(os.path.join(os.path.abspath('..'), "lib")) + +import config +from rest_requests.request import get_request, post_request, big_post_request +from parse_args import parse_args +from args.digest_login import DigestLogin +import yaml + + +def main(): + """ + Duplicate the events on the specified Opencast system defined in the yaml file + """ + + target_url, number_of_events, file_path = parse_args() + digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) + + target_url = target_url if target_url else config.target_url + number_of_duplicates = number_of_events if number_of_events else config.number_of_duplicates + file_path = file_path if file_path else config.yaml_file_path + + print("Starting duplication process.") + + # test API connection + url = f'{target_url}/api/info/me' + try: + get_request(url, digest_login, "events") + except: + __abort_script("Something went wrong. No Connection to API. Stopping script. ") + + # read IDs from file + event_ids = read_event_ids(file_path) + + # duplicate events + url = f'{target_url}/api/workflows/' + + for id in event_ids: + print(id) + data = { + 'event_identifier': id, + 'workflow_definition_identifier': 'duplicate-event', + 'configuration': '{"numberOfEvents":"{' + str(number_of_duplicates) + '}"}' + } + try: + response = post_request(url, digest_login, "events", data=data) + print(response) + except Exception as e: + print(str(e)) + __abort_script("Something went wrong. Could not duplicate event. Stopping script") + + print("Done.") + + +def read_event_ids(path): + with open(path, 'r') as f: + data = yaml.safe_load(f) + return data['event-IDs'] + + +def __abort_script(message): + print(message) + sys.exit() + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print("\nAborting process.") + sys.exit(0) diff --git a/create_oc_testdata/duplicate_events/parse_args.py b/create_oc_testdata/duplicate_events/parse_args.py new file mode 100644 index 0000000..d9f8d56 --- /dev/null +++ b/create_oc_testdata/duplicate_events/parse_args.py @@ -0,0 +1,32 @@ +from args.args_parser import get_args_parser +from args.args_error import args_error + + +MAX_NUMBER_OF_DUPLICATES = 20 + + +def parse_args(): + """ + Parse the arguments and check them for correctness + + :return: target_url and number of events to be created if given + :rtype: str, int + """ + parser, optional_args, required_args = get_args_parser() + + optional_args.add_argument("-t", "--target_url", type=str, nargs='+', help="URL of target system") + optional_args.add_argument("-n", "--number", type=int, nargs='+', help="number of duplicates per event") + optional_args.add_argument("-f", "--file", type=str, nargs='+', help="path to yaml file containing the event IDs") + + args = parser.parse_args() + + if not args.target_url: + args.target_url = [None] + if not args.number: + args.number = [None] + elif args.number[0] > MAX_NUMBER_OF_DUPLICATES: + args_error(parser, "to many duplicates ...") + if not args.file: + args.file = [None] + + return args.target_url[0], args.number[0], args.file[0] diff --git a/create_oc_testdata/generate_events/README.md b/create_oc_testdata/generate_events/README.md new file mode 100644 index 0000000..35dcd22 --- /dev/null +++ b/create_oc_testdata/generate_events/README.md @@ -0,0 +1,42 @@ +# Script for populating an Opencast system with test data + +This script creates random samples of Events (and Series) on the specified Opencast system. + +## How to Use + +### Configuration + +The script is configured by editing the values in `config.py`: + +| Configuration Key | Description | Default/Example | +| :---------------- | :---------------------------------------- | :--------------------------- | +| `target_url` | The server URL of the target tenant | https://tenant2.opencast.com | +| `digest_user` | The user name of the digest user | opencast_system_account | +| `digest_pw` | The password of the digest user | CHANGE_ME | +| `workflow_id` | The id of the workflow to start on ingest | reimport-workflow | +| `workflow_config` | The configuration for that workflow | {"autopublish": "false"} | + + +The configured digest user needs to exist on the specified Opencast system. + +### Usage + +The script can be called with the following parameters (all parameters in brackets are optional): + +`main.py [-t TARGET_URL ] [-n NUMBER_OF_EVENTS ]` + +| Short Option | Long Option | Description | +| :----------: | :---------- | :----------------------------------------- | +| `-t` | `--target` | The target url of the Opencast system | +| `-n` | `--number` | The number of Events to be created | +| `-f` | `--file` | The path to a test video | + +#### Usage example + +`main.py -t localhost:8080 -n 10 -f home/test/video.mp3` + +## Requirements + +This script was written for Python 3.8. + +It uses modules contained in the _lib_ directory. diff --git a/create_oc_testdata/config.py b/create_oc_testdata/generate_events/config.py similarity index 62% rename from create_oc_testdata/config.py rename to create_oc_testdata/generate_events/config.py index 067e36e..147c6b6 100644 --- a/create_oc_testdata/config.py +++ b/create_oc_testdata/generate_events/config.py @@ -4,16 +4,15 @@ target_url = "http://localhost:8080" # default path to a test video -test_video_path = '/home/malte/IdeaProjects/helper-scripts/create_oc_testdata/test_video.mkv' +test_video_path = '../create_oc_testdata/data/test_video.mkv' +# default path where to store the event IDs +yaml_file_path = '../create_oc_testdata/data/event_ids.yaml' # default value for the number of events to be created -number_of_events = 6 +number_of_events = 4 # default value for the number of series to be created number_of_series = 2 -# default value for the number of copies per event to be created -number_of_copies = 2 # digest login digest_user = "opencast_system_account" digest_pw = "CHANGE_ME" - diff --git a/create_oc_testdata/main.py b/create_oc_testdata/generate_events/main.py similarity index 63% rename from create_oc_testdata/main.py rename to create_oc_testdata/generate_events/main.py index 0c0427c..4b47de0 100644 --- a/create_oc_testdata/main.py +++ b/create_oc_testdata/generate_events/main.py @@ -5,9 +5,9 @@ import config from rest_requests.request import get_request, post_request, big_post_request -from rest_requests.request_error import RequestError from parse_args import parse_args from args.digest_login import DigestLogin +import yaml import string import random @@ -20,15 +20,9 @@ def main(): target_url, number_of_events, file_path = parse_args() digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) - if not target_url: - target_url = config.target_url - if not number_of_events: - number_of_events = config.number_of_events - if not file_path: - file_path = config.test_video_path - - print(number_of_events) - print(target_url) + target_url = target_url if target_url else config.target_url + number_of_events = number_of_events if number_of_events else config.number_of_events + file_path = file_path if file_path else config.test_video_path print("Starting population process.") @@ -40,15 +34,13 @@ def main(): __abort_script("Something went wrong. No Connection to API. Stopping script. ") # Serien erstellen - series = [] - + series_ids = [] for i in range(config.number_of_series): - url = f'{target_url}/series/' - id = __generate_random_name() - series.append(id) + series_id = "Series-ID-" + __generate_random_name() + series_ids.append(series_id) data = { - 'identifier': id, + 'identifier': series_id, 'publisher': __generate_random_name(), 'title': __generate_random_name(), 'acl': '{"acl": {"ace": [' @@ -56,64 +48,67 @@ def main(): '{"allow": true,"role": "ROLE_ADMIN","action": "read"}' ']}}' } - try: - response = post_request(url, digest_login, "series", data=data) - print(response) - + print(post_request(url, digest_login, "series", data=data)) + # print(response) except Exception as e: - print("ERROR") print(str(e)) __abort_script("Something went wrong. Could not create series. Stopping script") # Events erstellen + event_ids = [] + url = f'{target_url}/ingest/addMediaPackage' + files = [file_path] + for i in range(number_of_events): - url = f'{target_url}/ingest/addMediaPackage' # schedule-and-upload - files = [file_path] + event_id = "ID-" + __generate_random_name(length=5) + event_ids.append(event_id) data = { 'creator': __generate_random_name(), 'title': __generate_random_name(), 'flavor': 'presentation/source', 'description': 'This is a test description. This Event is only for testing purposes. ', + 'identifier': event_id, 'spatial': __generate_random_name(), - 'isPartOf': random.choice(series), - 'acl': '{"acl": {"ace": [' - '{"allow": true,"role": "ROLE_ADMIN","action": "read"}, ' - '{"allow": true,"role": "ROLE_ADMIN","action": "write"}, ' - '{"allow": true,"role": "ROLE_ANONYMOUS","action": "read"}' - ']}}' + 'isPartOf': random.choice(series_ids), + 'acl': '{"acl": {"ace": [' + '{"allow": true,"role": "ROLE_ADMIN","action": "read"}, ' + '{"allow": true,"role": "ROLE_ADMIN","action": "write"}' + ']}}' } try: response = big_post_request(url, digest_login, "events", data=data, files=files) print(response) - except Exception as e: - print("ERROR") print(str(e)) __abort_script("Something went wrong. Could not create event. Stopping script") - # ToDo copy the Events + # write event IDs to yaml file + yaml_content = {'event-IDs': event_ids} + with open(config.yaml_file_path, 'w') as file: + yaml.dump(yaml_content, file) print("Done.") def __create_alphabet(): alphabet = list(string.ascii_letters) - for i in range(10): - alphabet.append(str(i)) - for c in ['Ä','Ö','Ü','ä','ö','ü']: - alphabet.append(c) + # for i in range(10): + # alphabet.append(str(i)) + # for c in ['Ä','Ö','Ü','ä','ö','ü']: + # alphabet.append(c) return alphabet alphabet = __create_alphabet() -def __generate_random_name(): +def __generate_random_name(length=False): name = '' - for i in range(random.randint(1, 30)): + length = length if length else random.randint(1, 30) + for i in range(length): name += random.choice(alphabet) return name diff --git a/create_oc_testdata/parse_args.py b/create_oc_testdata/generate_events/parse_args.py similarity index 97% rename from create_oc_testdata/parse_args.py rename to create_oc_testdata/generate_events/parse_args.py index 9629c68..7e063d4 100644 --- a/create_oc_testdata/parse_args.py +++ b/create_oc_testdata/generate_events/parse_args.py @@ -2,7 +2,7 @@ from args.args_error import args_error -MAX_NUMBER_OF_EVENTS = 1000 +MAX_NUMBER_OF_EVENTS = 2000 def parse_args(): From ae75555c7f23da83872e0b48c6290a52a1d2bc4d Mon Sep 17 00:00:00 2001 From: mheyen Date: Tue, 6 Jul 2021 12:05:48 +0200 Subject: [PATCH 08/10] fixed format for request configuration --- create_oc_testdata/duplicate_events/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_oc_testdata/duplicate_events/main.py b/create_oc_testdata/duplicate_events/main.py index 55b408c..b20835b 100644 --- a/create_oc_testdata/duplicate_events/main.py +++ b/create_oc_testdata/duplicate_events/main.py @@ -42,7 +42,7 @@ def main(): data = { 'event_identifier': id, 'workflow_definition_identifier': 'duplicate-event', - 'configuration': '{"numberOfEvents":"{' + str(number_of_duplicates) + '}"}' + 'configuration': '{"numberOfEvents":"' + str(number_of_duplicates) + '"}' } try: response = post_request(url, digest_login, "events", data=data) From 2dbdbe7c7fe26218423e62e42b5c87e301821f34 Mon Sep 17 00:00:00 2001 From: mheyen Date: Tue, 6 Jul 2021 14:56:12 +0200 Subject: [PATCH 09/10] Updated README --- create_oc_testdata/duplicate_events/README.md | 34 +++++++++---------- create_oc_testdata/duplicate_events/main.py | 6 ++-- .../duplicate_events/parse_args.py | 13 +++---- create_oc_testdata/generate_events/README.md | 20 ++++++----- create_oc_testdata/generate_events/main.py | 23 +++++-------- .../generate_events/parse_args.py | 13 +++---- 6 files changed, 51 insertions(+), 58 deletions(-) diff --git a/create_oc_testdata/duplicate_events/README.md b/create_oc_testdata/duplicate_events/README.md index 35dcd22..0a992cc 100644 --- a/create_oc_testdata/duplicate_events/README.md +++ b/create_oc_testdata/duplicate_events/README.md @@ -1,20 +1,20 @@ -# Script for populating an Opencast system with test data +# Script for duplicating events on an Opencast system -This script creates random samples of Events (and Series) on the specified Opencast system. +This script duplicates the specified Events on the Opencast system. ## How to Use ### Configuration -The script is configured by editing the values in `config.py`: +The script can be configured by editing the values in `config.py`: -| Configuration Key | Description | Default/Example | -| :---------------- | :---------------------------------------- | :--------------------------- | -| `target_url` | The server URL of the target tenant | https://tenant2.opencast.com | -| `digest_user` | The user name of the digest user | opencast_system_account | -| `digest_pw` | The password of the digest user | CHANGE_ME | -| `workflow_id` | The id of the workflow to start on ingest | reimport-workflow | -| `workflow_config` | The configuration for that workflow | {"autopublish": "false"} | +| Configuration Key | Description | Default/Example | +| :------------------------ | :------------------------------------------- | :---------------------------------------- | +| `target_url` | The server URL of the target tenant | http://localhost:8080 | +| `yaml_file_path` | The path to the yaml file with the event IDs | ../create_oc_testdata/data/event_ids.yaml | +| `number_of_duplicates` | The number of duplicates per event | 2 | +| `digest_user` | The user name of the digest user | opencast_system_account | +| `digest_pw` | The password of the digest user | CHANGE_ME | The configured digest user needs to exist on the specified Opencast system. @@ -23,17 +23,17 @@ The configured digest user needs to exist on the specified Opencast system. The script can be called with the following parameters (all parameters in brackets are optional): -`main.py [-t TARGET_URL ] [-n NUMBER_OF_EVENTS ]` +`python main.py [-t TARGET_URL ] [-n NUMBER_OF_DUPLICATES ] [-f YAML_FILE_PATH]` -| Short Option | Long Option | Description | -| :----------: | :---------- | :----------------------------------------- | -| `-t` | `--target` | The target url of the Opencast system | -| `-n` | `--number` | The number of Events to be created | -| `-f` | `--file` | The path to a test video | +| Short Option | Long Option | Description | +| :----------: | :---------- | :----------------------------------------------- | +| `-t` | `--target` | The target url of the Opencast system | +| `-n` | `--number` | The number of duplicates to be created | +| `-f` | `--file` | The path to the file containing the event IDs | #### Usage example -`main.py -t localhost:8080 -n 10 -f home/test/video.mp3` +`python main.py -t localhost:8080 -n 2 -f data/event_ids.yaml` ## Requirements diff --git a/create_oc_testdata/duplicate_events/main.py b/create_oc_testdata/duplicate_events/main.py index b20835b..ecc00fc 100644 --- a/create_oc_testdata/duplicate_events/main.py +++ b/create_oc_testdata/duplicate_events/main.py @@ -12,12 +12,14 @@ def main(): """ - Duplicate the events on the specified Opencast system defined in the yaml file + Duplicate the events on a specified Opencast system """ - target_url, number_of_events, file_path = parse_args() + # create digest login digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) + # parse args + target_url, number_of_events, file_path = parse_args() target_url = target_url if target_url else config.target_url number_of_duplicates = number_of_events if number_of_events else config.number_of_duplicates file_path = file_path if file_path else config.yaml_file_path diff --git a/create_oc_testdata/duplicate_events/parse_args.py b/create_oc_testdata/duplicate_events/parse_args.py index d9f8d56..aed0e46 100644 --- a/create_oc_testdata/duplicate_events/parse_args.py +++ b/create_oc_testdata/duplicate_events/parse_args.py @@ -20,13 +20,10 @@ def parse_args(): args = parser.parse_args() - if not args.target_url: - args.target_url = [None] - if not args.number: - args.number = [None] - elif args.number[0] > MAX_NUMBER_OF_DUPLICATES: - args_error(parser, "to many duplicates ...") - if not args.file: - args.file = [None] + args.target_url = args.target_url if args.target_url else [None] + args.number = args.number if args.number else [None] + if args.number[0] and args.number[0] > MAX_NUMBER_OF_DUPLICATES: + args_error(parser, "too many duplicates ...") + args.file = args.file if args.file else [None] return args.target_url[0], args.number[0], args.file[0] diff --git a/create_oc_testdata/generate_events/README.md b/create_oc_testdata/generate_events/README.md index 35dcd22..9d5735c 100644 --- a/create_oc_testdata/generate_events/README.md +++ b/create_oc_testdata/generate_events/README.md @@ -8,13 +8,15 @@ This script creates random samples of Events (and Series) on the specified Openc The script is configured by editing the values in `config.py`: -| Configuration Key | Description | Default/Example | -| :---------------- | :---------------------------------------- | :--------------------------- | -| `target_url` | The server URL of the target tenant | https://tenant2.opencast.com | -| `digest_user` | The user name of the digest user | opencast_system_account | -| `digest_pw` | The password of the digest user | CHANGE_ME | -| `workflow_id` | The id of the workflow to start on ingest | reimport-workflow | -| `workflow_config` | The configuration for that workflow | {"autopublish": "false"} | +| Configuration Key | Description | Default/Example | +| :----------------- | :------------------------------------------ | :---------------------------------------- | +| `target_url` | The server URL of the target tenant | http://localhost:8080 | +| `test_video_path` | The path to a test video to be used | ../create_oc_testdata/data/test_video.mkv | +| `yaml_file_path` | The path to the file to store the ecent IDs | ../create_oc_testdata/data/event_ids.yaml | +| `number_of_events` | The number of Events to be created | 4 | +| `number_of_series` | The number of Series to be created | 2 | +| `digest_user` | The user name of the digest user | opencast_system_account | +| `digest_pw` | The password of the digest user | CHANGE_ME | The configured digest user needs to exist on the specified Opencast system. @@ -23,7 +25,7 @@ The configured digest user needs to exist on the specified Opencast system. The script can be called with the following parameters (all parameters in brackets are optional): -`main.py [-t TARGET_URL ] [-n NUMBER_OF_EVENTS ]` +`python main.py [-t TARGET_URL ] [-n NUMBER_OF_EVENTS ] [-f TEST_VIDEO_PATH]` | Short Option | Long Option | Description | | :----------: | :---------- | :----------------------------------------- | @@ -33,7 +35,7 @@ The script can be called with the following parameters (all parameters in bracke #### Usage example -`main.py -t localhost:8080 -n 10 -f home/test/video.mp3` +`python main.py -t localhost:8080 -n 10 -f home/test/video.mp3` ## Requirements diff --git a/create_oc_testdata/generate_events/main.py b/create_oc_testdata/generate_events/main.py index 4b47de0..aaa5810 100644 --- a/create_oc_testdata/generate_events/main.py +++ b/create_oc_testdata/generate_events/main.py @@ -17,9 +17,11 @@ def main(): Populate the specified Opencast system with random sample events """ - target_url, number_of_events, file_path = parse_args() + # create digest login digest_login = DigestLogin(user=config.digest_user, password=config.digest_pw) + # parse args + target_url, number_of_events, file_path = parse_args() target_url = target_url if target_url else config.target_url number_of_events = number_of_events if number_of_events else config.number_of_events file_path = file_path if file_path else config.test_video_path @@ -50,7 +52,6 @@ def main(): } try: print(post_request(url, digest_login, "series", data=data)) - # print(response) except Exception as e: print(str(e)) __abort_script("Something went wrong. Could not create series. Stopping script") @@ -59,12 +60,9 @@ def main(): event_ids = [] url = f'{target_url}/ingest/addMediaPackage' files = [file_path] - for i in range(number_of_events): - event_id = "ID-" + __generate_random_name(length=5) event_ids.append(event_id) - data = { 'creator': __generate_random_name(), 'title': __generate_random_name(), @@ -78,28 +76,25 @@ def main(): '{"allow": true,"role": "ROLE_ADMIN","action": "write"}' ']}}' } - try: - response = big_post_request(url, digest_login, "events", data=data, files=files) - print(response) + print(big_post_request(url, digest_login, "events", data=data, files=files)) except Exception as e: print(str(e)) __abort_script("Something went wrong. Could not create event. Stopping script") # write event IDs to yaml file - yaml_content = {'event-IDs': event_ids} with open(config.yaml_file_path, 'w') as file: - yaml.dump(yaml_content, file) + yaml.dump({'event-IDs': event_ids}, file) print("Done.") def __create_alphabet(): alphabet = list(string.ascii_letters) - # for i in range(10): - # alphabet.append(str(i)) - # for c in ['Ä','Ö','Ü','ä','ö','ü']: - # alphabet.append(c) + for i in range(10): + alphabet.append(str(i)) + for c in ['Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü']: + alphabet.append(c) return alphabet alphabet = __create_alphabet() diff --git a/create_oc_testdata/generate_events/parse_args.py b/create_oc_testdata/generate_events/parse_args.py index 7e063d4..1c4e2b7 100644 --- a/create_oc_testdata/generate_events/parse_args.py +++ b/create_oc_testdata/generate_events/parse_args.py @@ -20,13 +20,10 @@ def parse_args(): args = parser.parse_args() - if not args.target_url: - args.target_url = [None] - if not args.number: - args.number = [None] - elif args.number[0] > MAX_NUMBER_OF_EVENTS: - args_error(parser, "to many events ...") - if not args.file: - args.file = [None] + args.target_url = args.target_url if args.target_url else [None] + args.number = args.number if args.number else [None] + if args.number[0] and args.number[0] > MAX_NUMBER_OF_EVENTS: + args_error(parser, "too many events ...") + args.file = args.file if args.file else [None] return args.target_url[0], args.number[0], args.file[0] From edae5d91ab0ce3f9b2f175a7b805b0bd055806e4 Mon Sep 17 00:00:00 2001 From: mheyen Date: Wed, 7 Jul 2021 13:31:53 +0200 Subject: [PATCH 10/10] removed 'Umlaute' from random names --- create_oc_testdata/generate_events/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/create_oc_testdata/generate_events/main.py b/create_oc_testdata/generate_events/main.py index aaa5810..3e89ce2 100644 --- a/create_oc_testdata/generate_events/main.py +++ b/create_oc_testdata/generate_events/main.py @@ -91,10 +91,10 @@ def main(): def __create_alphabet(): alphabet = list(string.ascii_letters) - for i in range(10): - alphabet.append(str(i)) - for c in ['Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü']: - alphabet.append(c) + # for i in range(10): + # alphabet.append(str(i)) + # for c in ['Ä', 'Ö', 'Ü', 'ä', 'ö', 'ü']: + # alphabet.append(c) return alphabet alphabet = __create_alphabet()