diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b2eea3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +config.* +config.env +.env +config +data +cache + diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..ee3a1b9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:alpine + +RUN pip install --upgrade pip + +RUN pip install flask + +RUN pip install flask_restful + +RUN pip install python-dateutil + +RUN pip install realog + +EXPOSE 80 + +ADD src /application/ +WORKDIR /application/ +CMD ["python", "-u", "./app_video.py"] + + + diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100755 index 0000000..78fedb1 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '3' +services: + REST_video_service: + build: . + restart: always + image: yui.heero/video_rest_api + container_name: video_rest_api + ports: + - 15080:80 + diff --git a/src/__pycache__/data_global_elements.cpython-38.pyc b/src/__pycache__/data_global_elements.cpython-38.pyc new file mode 100644 index 0000000..a1ec7e1 Binary files /dev/null and b/src/__pycache__/data_global_elements.cpython-38.pyc differ diff --git a/src/__pycache__/data_interface.cpython-38.pyc b/src/__pycache__/data_interface.cpython-38.pyc new file mode 100644 index 0000000..506395f Binary files /dev/null and b/src/__pycache__/data_interface.cpython-38.pyc differ diff --git a/src/__pycache__/tools.cpython-38.pyc b/src/__pycache__/tools.cpython-38.pyc new file mode 100644 index 0000000..c390417 Binary files /dev/null and b/src/__pycache__/tools.cpython-38.pyc differ diff --git a/src/app_video.py b/src/app_video.py new file mode 100755 index 0000000..2abac79 --- /dev/null +++ b/src/app_video.py @@ -0,0 +1,280 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2019, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## +#pip install flask --user +#pip install flask_restful --user +#pip install python-dateutil --user + +from flask import Flask +from flask_restful import Api, Resource, reqparse +import dateutil.parser + + +import time +import os +import sys +import datetime +import time, threading +import realog.debug as debug + +import config +import tools +import data_interface +import data_global_elements + + +debug.info("connect BDD interface"); + + + +app = Flask(__name__) +api = Api(app) + +def add_interface(_name): + data_global_elements.add_interface(_name, data_interface.DataInterface(_name, os.path.join(tools.get_run_path(), "data", "bdd_" + _name + ".json"))) + +API_THEME = "theme" +add_interface(API_THEME) +API_GROUP = "group" +add_interface(API_GROUP) +API_SAISON = "saison" +add_interface(API_SAISON) +API_VIDEO = "video" +add_interface(API_VIDEO) + +""" +LIST_themes = [ + { + 'id': 0, + 'name': 'Documentary', + 'description': 'Documentary (annimals, space, earth...)', + },{ + 'id': 1, + 'name': 'Movie', + 'description': 'Movie with real humans (film)', + },{ + 'id': 2, + 'name': 'Annimation', + 'description': 'Annimation movies (film)', + },{ + 'id': 3, + 'name': 'Short Films', + 'description': 'Small movies (less 2 minutes)', + },{ + 'id': 4, + 'name': 'tv show', + 'description': 'Tv show form old peoples', + }, { + 'id': 5, + 'name': 'Anniation tv show', + 'description': 'Tv show form young peoples', + }, +] + + +class ThemeList(Resource): + # example use: curl http://127.0.0.1:15080/api/v1/theme + def get(self): + debug.info("Request temes: " + str(time)) + return LIST_themes, 200 + +class Theme(Resource): + # example use: curl http://127.0.0.1:15080/api/v1/theme/xxx + def get(self, id): + debug.info("Request theme: " + str(id)) + for elem in LIST_themes: + if 'id' in elem.keys() \ + and elem["id"] == id: + return elem, 200 + return "No data found in list of element: " + str(len(LIST_themes)), 404 +""" + +class ThemeList(Resource): + def __init__(self): + self.name = API_THEME + + # example use: curl http://127.0.0.1:15080/api/v1/theme + def get(self): + return data_global_elements.get_interface(self.name).gets(), 200 + + """ + def post(self): + args = parser.parse_args() + todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1 + todo_id = 'todo%i' % todo_id + TODOS[todo_id] = {'task': args['task']} + return TODOS[todo_id], 201 + """ + +class Theme(Resource): + def __init__(self): + self.name = API_THEME + + # example use: curl http://127.0.0.1:15080/api/v1/theme/xxx + def get(self, id): + value = data_global_elements.get_interface(self.name).get(id) + if value != None: + return value, 200 + return "No data found", 404 + + def delete(self, id): + ret = data_global_elements.get_interface(self.name).delete(id) + if ret == True: + return '', 204 + return "No data found", 404 + """ + def put(self, id): + ret = data_global_elements.get_interface(self.name).put(id) + return task, 201 + """ + +api.add_resource(ThemeList, "/api/v1/" + API_THEME) +api.add_resource(Theme, "/api/v1/" + API_THEME + "/") + +#--------------------------------------------------------------------------------------- + +class GroupList(Resource): + def __init__(self): + self.name = API_GROUP + + # example use: curl http://127.0.0.1:15080/api/v1/Group + def get(self): + return data_global_elements.get_interface(self.name).gets(), 200 + + """ + def post(self): + args = parser.parse_args() + todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1 + todo_id = 'todo%i' % todo_id + TODOS[todo_id] = {'task': args['task']} + return TODOS[todo_id], 201 + """ + +class Group(Resource): + def __init__(self): + self.name = API_GROUP + + # example use: curl http://127.0.0.1:15080/api/v1/Group/xxx + def get(self, id): + value = data_global_elements.get_interface(self.name).get(id) + if value != None: + return value, 200 + return "No data found", 404 + + def delete(self, id): + ret = data_global_elements.get_interface(self.name).delete(id) + if ret == True: + return '', 204 + return "No data found", 404 + """ + def put(self, id): + ret = data_global_elements.get_interface(self.name).put(id) + return task, 201 + """ + +api.add_resource(GroupList, "/api/v1/" + API_GROUP) +api.add_resource(Group, "/api/v1/" + API_GROUP + "/") + +#--------------------------------------------------------------------------------------- + +class SaisonList(Resource): + def __init__(self): + self.name = API_SAISON + + # example use: curl http://127.0.0.1:15080/api/v1/Saison + def get(self): + return data_global_elements.get_interface(self.name).gets(), 200 + + """ + def post(self): + args = parser.parse_args() + todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1 + todo_id = 'todo%i' % todo_id + TODOS[todo_id] = {'task': args['task']} + return TODOS[todo_id], 201 + """ + +class Saison(Resource): + def __init__(self): + self.name = API_SAISON + + # example use: curl http://127.0.0.1:15080/api/v1/Saison/xxx + def get(self, id): + value = data_global_elements.get_interface(self.name).get(id) + if value != None: + return value, 200 + return "No data found", 404 + + def delete(self, id): + ret = data_global_elements.get_interface(self.name).delete(id) + if ret == True: + return '', 204 + return "No data found", 404 + """ + def put(self, id): + ret = data_global_elements.get_interface(self.name).put(id) + return task, 201 + """ + +api.add_resource(SaisonList, "/api/v1/" + API_SAISON) +api.add_resource(Saison, "/api/v1/" + API_SAISON + "/") + +#--------------------------------------------------------------------------------------- + +class VideoList(Resource): + def __init__(self): + self.name = API_VIDEO + + # example use: curl http://127.0.0.1:15080/api/v1/Video + def get(self): + return data_global_elements.get_interface(self.name).gets(), 200 + + """ + def post(self): + args = parser.parse_args() + todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1 + todo_id = 'todo%i' % todo_id + TODOS[todo_id] = {'task': args['task']} + return TODOS[todo_id], 201 + """ + +class Video(Resource): + def __init__(self): + self.name = API_VIDEO + + # example use: curl http://127.0.0.1:15080/api/v1/Video/xxx + def get(self, id): + value = data_global_elements.get_interface(self.name).get(id) + if value != None: + return value, 200 + return "No data found", 404 + + def delete(self, id): + ret = data_global_elements.get_interface(self.name).delete(id) + if ret == True: + return '', 204 + return "No data found", 404 + """ + def put(self, id): + ret = data_global_elements.get_interface(self.name).put(id) + return task, 201 + """ + +api.add_resource(VideoList, "/api/v1/" + API_VIDEO) +api.add_resource(Video, "/api/v1/" + API_VIDEO + "/") + + +rest_config = config.get_rest_config() + +debug.info("Start REST application: " + str(rest_config["host"]) + ":" + str(rest_config["port"])) + +app.run(debug=False, host=rest_config["host"], port=str(rest_config["port"])) + +debug.info("END program"); +sys.exit(0) diff --git a/src/data_global_elements.py b/src/data_global_elements.py new file mode 100644 index 0000000..9b5e1bd --- /dev/null +++ b/src/data_global_elements.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2019, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## + +interfaces = {} + +def get_list_interface(): + global interfaces + return interfaces + +def get_interface(_name): + global interfaces + return interfaces[_name] + +def add_interface(_name, _interface): + global interfaces + interfaces[_name] = _interface + + +import time, threading +def check_save(): + print(time.ctime()) + for elem in interfaces.keys(): + interfaces[elem].check_save() + threading.Timer(10, check_save).start() + +check_save() + diff --git a/src/data_interface.py b/src/data_interface.py new file mode 100644 index 0000000..2d5b675 --- /dev/null +++ b/src/data_interface.py @@ -0,0 +1,94 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2019, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## + +global_id = 0; + +import tools +import json +from realog import debug + +class DataInterface(): + def __init__(self, _name, _file): + self.name = _name + self.file = _file + self.bdd = [] + self.need_save = False + if tools.exist(self.file) == False: + self.need_save = True + else: + data = tools.file_read_data(self.file) + self.bdd = json.loads(data) + + def upgrade_global_bd_id(self): + global global_id + for elem in self.bdd: + if 'id' not in elem.keys(): + continue + if elem["id"] >= global_id: + global_id = elem["id"] + 1 + + def get_table_index(id): + id_in_bdd = 0 + for elem in self.bdd: + if 'id' in elem.keys() \ + and elem["id"] == id: + return id_in_bdd + id_in_bdd += 1 + return None + + def check_save(self): + if self.need_save == False: + return + debug.warning("Save bdd: " + self.file) + data = json.dumps(self.bdd, sort_keys=True, indent=4) + self.need_save = False + tools.file_write_data_safe(self.file, data) + + def gets(self): + debug.info("gets " + self.name) + return self.bdd + + def get(self, id): + debug.info("get " + self.name + ": " + str(id)) + for elem in self.bdd: + if 'id' in elem.keys() \ + and elem["id"] == id: + return elem + return None + + def delete(self, id): + debug.info("delete " + self.name + ": " + str(id)) + id_in_bdd = self.get_table_index(id) + if id_in_bdd == None: + return False + del self.bdd[id_in_bdd] + self.need_save = True + return True + + def put(self, id, value): + debug.info("put " + self.name + ": " + str(id)) + id_in_bdd = self.get_table_index(id) + if id_in_bdd == None: + return False + value["id"] = id + self.bdd[id_in_bdd] = value + self.need_save = True + return True + + def post(self, value): + debug.info("post " + self.name) + global global_id + value["id"] = global_id + global_id += 1 + self.bdd.append(value) + self.need_save = True + return value + + diff --git a/src/tools.py b/src/tools.py new file mode 100644 index 0000000..9851f86 --- /dev/null +++ b/src/tools.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## +## @author Edouard DUPIN +## +## @copyright 2012, Edouard DUPIN, all right reserved +## +## @license MPL v2.0 (see license file) +## + +import os +import shutil +import errno +import fnmatch +import stat +# Local import +from realog import debug + +""" + +""" +def get_run_path(): + return os.getcwd() + +""" + +""" +def get_current_path(file): + return os.path.dirname(os.path.realpath(file)) + +def create_directory_of_file(file): + debug.info("Create directory of path: '" + file + "'") + path = os.path.dirname(file) + debug.info("Create directory: '" + path + "'") + try: + os.stat(path) + except: + os.makedirs(path) + +def get_list_sub_path(path): + # TODO : os.listdir(path) + for dirname, dirnames, filenames in os.walk(path): + return dirnames + return [] + +def remove_path_and_sub_path(path): + if os.path.isdir(path): + debug.verbose("remove path : '" + path + "'") + shutil.rmtree(path) + +def remove_file(path): + if os.path.isfile(path): + os.remove(path) + elif os.path.islink(path): + os.remove(path) + +def exist(path): + if os.path.isdir(path): + return True + if os.path.isfile(path): + return True + if os.path.islink(path): + return True + return False + +def file_size(path): + if not os.path.isfile(path): + return 0 + statinfo = os.stat(path) + return statinfo.st_size + +def file_read_data(path, binary=False): + debug.verbose("path= " + path) + if not os.path.isfile(path): + return "" + if binary == True: + file = open(path, "rb") + else: + file = open(path, "r") + data_file = file.read() + file.close() + return data_file + +def version_to_string(version): + version_ID = "" + for id in version: + if len(version_ID) != 0: + if type(id) == str: + version_ID += "-" + else: + version_ID += "." + version_ID += str(id) + return version_ID + +## +## @brief Write data in a specific path. +## @param[in] path Path of the data might be written. +## @param[in] data Data To write in the file. +## @param[in] only_if_new (default: False) Write data only if data is different. +## @return True Something has been copied +## @return False Nothing has been copied +## +def file_write_data(path, data, only_if_new=False): + if only_if_new == True: + if os.path.exists(path) == True: + old_data = file_read_data(path) + if old_data == data: + return False + #real write of data: + create_directory_of_file(path) + file = open(path, "w") + file.write(data) + file.close() + return True + +def file_write_data_safe(path, data): + #real write of data: + create_directory_of_file(path) + file = open(path + ".tmp", "w") + file.write(data) + file.close() + shutil.move(path + ".tmp", path) + return True + +def list_to_str(list): + if type(list) == type(str()): + return list + " " + else: + result = "" + # mulyiple imput in the list ... + for elem in list: + result += list_to_str(elem) + return result