diff --git a/Doxyfile b/Doxyfile index 3d572b1..8192206 100644 --- a/Doxyfile +++ b/Doxyfile @@ -260,7 +260,7 @@ OPTIMIZE_OUTPUT_FOR_C = NO # qualified scopes will look different, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_JAVA = YES +OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. @@ -435,25 +435,25 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. -EXTRACT_PACKAGE = NO +EXTRACT_PACKAGE = YES # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = NO +EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, @@ -469,7 +469,7 @@ EXTRACT_LOCAL_CLASSES = YES # included. # The default value is: NO. -EXTRACT_LOCAL_METHODS = NO +EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called @@ -478,7 +478,7 @@ EXTRACT_LOCAL_METHODS = NO # are hidden. # The default value is: NO. -EXTRACT_ANON_NSPACES = NO +EXTRACT_ANON_NSPACES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these @@ -524,7 +524,7 @@ INTERNAL_DOCS = NO # and Mac users are advised to set this option to NO. # The default value is: system dependent. -CASE_SENSE_NAMES = NO +CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the @@ -778,7 +778,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = warn.txt +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -846,6 +846,7 @@ FILE_PATTERNS = *.c \ *.md \ *.mm \ *.dox \ + *.js \ *.py \ *.pyw \ *.f90 \ @@ -858,8 +859,7 @@ FILE_PATTERNS = *.c \ *.vhd \ *.vhdl \ *.ucf \ - *.qsf \ - *.js \ + *.qsf # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -874,7 +874,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = gen-doc/* +EXCLUDE = gen-doc # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -907,14 +907,14 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = README.md # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. -EXAMPLE_PATTERNS = * +EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands @@ -996,7 +996,7 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -1071,25 +1071,6 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse-libclang=ON option for CMake. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1479,7 +1460,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. @@ -1667,7 +1648,7 @@ EXTRA_SEARCH_MAPPINGS = # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = YES +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1866,7 +1847,7 @@ COMPACT_RTF = NO # The default value is: NO. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_HYPERLINKS = NO +RTF_HYPERLINKS = YES # Load stylesheet definitions from file. Syntax is similar to doxygen's config # file, i.e. a series of assignments. You only have to provide replacements, diff --git a/Files/game.js b/Files/game.js index ac086b4..42a3573 100644 --- a/Files/game.js +++ b/Files/game.js @@ -1,5 +1,7 @@ /** - * @file Files\game.js functions for game.html + * @file Files/game.js Implementation of @ref game + * @defgroup game Functions for game.js + * @addtogroup game * @{ */ @@ -124,42 +126,46 @@ function get_title() { /** * check if there is a need to move to the next part - * @return nothing + * @return nothing */ function check_move_to_next() { xmlrequest("check_move_next_page", function() { - if (this.readyState == 4 && this.status == 200) { - if (xmlstring_to_boolean(this.responseText)) { - switch (state) { - case "wait": - switch_screens(); - state = "question"; - get_title() - xmlrequest("moved_to_next_question", null); - break; - case "question": - switch_screens(); - state = "leaderboard"; - xmlrequest("moved_to_next_question", null); - break; - case "wait_question": - get_score(); - switch_screens(); - state = "leaderboard"; - xmlrequest("moved_to_next_question", null); - break; - case "leaderboard": - switch_screens(); - xmlrequest("moved_to_next_question", null); - state = "question"; - break; + if (this.readyState == 4) { + if (this.status == 200) { + if (xmlstring_to_boolean(this.responseText)) { + switch (state) { + case "wait": + get_title() + switch_screens(); + state = "question"; + xmlrequest("moved_to_next_question", null); + break; + case "question": + switch_screens(); + state = "leaderboard"; + xmlrequest("moved_to_next_question", null); + break; + case "wait_question": + get_score(); + switch_screens(); + state = "leaderboard"; + xmlrequest("moved_to_next_question", null); + break; + case "leaderboard": + get_title() + switch_screens(); + xmlrequest("moved_to_next_question", null); + state = "question"; + break; + } } } + setTimeout(check_move_to_next, 1000); } } ); - setTimeout(check_move_to_next, 1000); + } /** @} */ diff --git a/Files/home.js b/Files/home.js index 2ba7756..4a67b97 100644 --- a/Files/home.js +++ b/Files/home.js @@ -1,5 +1,7 @@ /** - * @file Files\home.js functions for game.html + * @file Files/home.js Implementation of @ref home + * @defgroup home Functions for home.js + * @addtogroup home * @{ */ diff --git a/Files/new.js b/Files/new.js index 02ff2a8..3166287 100644 --- a/Files/new.js +++ b/Files/new.js @@ -1,5 +1,7 @@ /** - * @file Files\new.js functions for game.html + * @file Files/new.js Implementation of @ref new + * @defgroup new Functions for new.js + * @addtogroup new * @{ */ diff --git a/Files/quiz.js b/Files/quiz.js index 500643e..b1ff53f 100644 --- a/Files/quiz.js +++ b/Files/quiz.js @@ -1,10 +1,13 @@ /** - * @file Files\quiz.js functions for game.html + * @file Files/quiz.js Implementation of @ref quiz + * @defgroup quiz Functions for quiz.js + * @addtogroup quiz * @{ */ + var PLAYERS_IN_LINE = 3; var state = "Registration"; -var timer = window.setInterval(getnames, 1000); +timer = window.setInterval(getnames, 1000); var number_of_questions = null; @@ -69,19 +72,21 @@ function get_join_number() { */ function getnames() { xmlrequest("getnames", function() { - if (this.readyState == 4 && this.status == 200) { - xmlDoc = parse_xml_from_string(this.responseText); - players = xmlDoc.getElementsByTagName("player"); - string_players = ""; - for (i = 0; i < players.length; i++) { - string_players += players[i].getAttribute("name"); - if (i % PLAYERS_IN_LINE === 0 && i !== 0) { - string_players += "
"; - } else if ((i + 1) < players.length) { - string_players += " "; + if (this.readyState == 4) { + if (this.status == 200) { + xmlDoc = parse_xml_from_string(this.responseText); + players = xmlDoc.getElementsByTagName("player"); + string_players = ""; + for (i = 0; i < players.length; i++) { + string_players += players[i].getAttribute("name"); + if (i % PLAYERS_IN_LINE === 0 && i !== 0) { + string_players += "
"; + } else if ((i + 1) < players.length) { + string_players += " "; + } } + document.getElementById("names").innerHTML = string_players; } - document.getElementById("names").innerHTML = string_players; } }); } @@ -99,25 +104,25 @@ function check_moveable() { * Switch from Registration screen to Opening screen */ function change_Registeration_Opening() { + clearInterval(timer); state = "Opening"; set_timer("5"); - clearInterval(timer); - timer = window.setInterval(check_timer_change, 1000); + check_timer_change() getinfo(); switch_screens(); } /** - * Check if need to change question + * Check if need to change from Question to part after it */ function check_move_question() { xmlrequest("check_move_question", function() { if (this.readyState == 4 && this.status == 200) { if (xmlstring_to_boolean(this.responseText)) { - //HACK: check if you can change it to to check_move_next_page insteed - clearInterval(timer); change_Question_Answer(); + } else { + setTimeout(check_move_question, 1000) } } } @@ -142,9 +147,8 @@ function change_Question_Answer() { } state = "Answer"; switch_screens(); - clearInterval(timer); set_timer("5"); - timer = window.setInterval(check_timer_change, 1000); + check_timer_change() } } ); @@ -190,7 +194,7 @@ function change_Answer_Leaderboard() { state = "Leaderboard"; switch_screens(); set_timer("5"); - timer = window.setInterval(check_timer_change, 1000); + check_timer_change(); } } ); @@ -249,25 +253,29 @@ function set_timer(new_time) { function check_timer_change() { xmlrequest("check_timer_change", function() { - if (this.readyState == 4 && this.status == 200) { - if (xmlstring_to_boolean(this.responseText)) { - clearInterval(timer); - if (state == "Opening") { - move_to_next_question(); - } - if (state == "Answer") { - change_Answer_Leaderboard(); - } - if (state == "Leaderboard") { - if (number_of_questions > -1) { + if (this.readyState == 4) { + if (this.status == 200) { + if (xmlstring_to_boolean(this.responseText)) { + if (state == "Opening") { move_to_next_question(); - } else { - xmlrequest("set_ended?new=True", null); - order_move_all_players(); - state = "Finish"; - // WIP } + if (state == "Answer") { + change_Answer_Leaderboard(); + } + if (state == "Leaderboard") { + if (number_of_questions > -1) { + move_to_next_question(); + } else { + xmlrequest("set_ended?new=True", null); + order_move_all_players(); + state = "Finish"; + } + } + } else { + setTimeout(check_timer_change, 1000); } + } else if (this.status == 500) { + setTimeout(check_timer_change, 1000); } } } @@ -291,7 +299,6 @@ function move_to_next_question() { state = "Finish"; get_winner(); switch_screens(); - clearInterval(timer); } } } @@ -308,7 +315,7 @@ function get_question() { xmlDoc = parse_xml_from_string(this.responseText); question = xmlDoc.getElementsByTagName("Question")[0]; set_timer(question.getAttribute("duration")); - timer = window.setInterval(check_move_question, 1000); + check_move_question(); document.getElementById("question_title").innerHTML = question.childNodes[1].textContent; var questionsID = ["A_answer", "B_answer", "C_answer", "D_answer"]; diff --git a/Files/util.js b/Files/util.js index 26969fd..d3e7157 100644 --- a/Files/util.js +++ b/Files/util.js @@ -1,5 +1,7 @@ /** - * @file Files\util.js Util file + * @file Files/util.js Implementation of @ref util + * @defgroup util Functions for util.js + * @addtogroup util * @{ */ diff --git a/README.md b/README.md index 24d2a12..0ab9c44 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -**PyHoot** +# PyHoot -Kahoot clone based on Python, final project for Gvahim. +*Kahoot clone based on Python, final project for Gvahim.* Warning: Python 2.7 must be installed on the system. @@ -8,15 +8,15 @@ if you don't have it have it, you can download it from here. [Link](https://www. **When you install in `Customize Python 2.7.x`, make sure you click on `Add python.exe to Path` and then on `Will be installed on local hard drive`.** -How to start the server: +## How to start the server: -If you are using windows (recommended for Windows users): +###If you are using windows (recommended for Windows users):### Go to the PyHoot directory and click on `Start Server (Windows).bat` from the PyHoot Directory. It will start the server on your IP address with port 80, meaning that you can join the game from any device in the same network as your server by writing the address in the address line. If you don't know your own IP address you can see it on the third line on the window that will be opened. -If you are using command line \ shell (recommended for experts and Linux users): +###If you are using command line \ shell (recommended for experts and Linux users):### Start the command line or shell in your system and change the directory where you put the directory of files (named PyHoot). Write the next command in the command line \ shell: @@ -28,6 +28,8 @@ This command will enable you to use different arguments than the default ones, y ```python -m PyHoot --[name of argument]=[the value of the arguement]``` In this way, you'll be able to add as many arguments as you want. +To shutdown the Robot use CTRL + Break + Arguments list: -h, --help show this help message and exit @@ -43,21 +45,3 @@ Arguments list: --log-level {DEBUG,INFO,WARNING,CRITICAL,ERROR} Log level --log-file FILE Logfile to write to, otherwise will log to console. - -TODO: -- [X] Make server send files -- [X] Create XML class -- [X] Quiz editor (independent) -- [X] Add support to services -- [X] Registrater players -- [X] Show question on the screen and move to the next part (timer) -- [X] Get answers and analyze them -- [X] Update database of player with score -- [X] Show answer -- [X] Show leaderboard -- [X] End of game -- [X] XML only -- [ ] CSS Day! -- [X] Python Client Random Answer -- [X] Python Client Fetch images -- [ ] Hide\ Find string in picture diff --git a/Star Server (Windows).bat b/Start Server (Windows).bat similarity index 100% rename from Star Server (Windows).bat rename to Start Server (Windows).bat diff --git a/__init__.py b/__init__.py index d0c1902..e085d4a 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1,3 @@ -"""Init file""" +## @package PyHoot.__init__ +# Initialization file +## @file __init__.py Implementation of @ref PyHoot.__init__ diff --git a/__main__.py b/__main__.py index 3587741..7243592 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,7 @@ -"""main file""" -## @file __main__.pymain file +## @package PyHoot.__main__ +# Main file +## @file __main__.py Implementation of @ref PyHoot.__main__ + import argparse import logging import os diff --git a/async_io.py b/async_io.py index a83e4bb..bb32422 100644 --- a/async_io.py +++ b/async_io.py @@ -1,5 +1,6 @@ -"""AsyncIO (with select_object or poll_object)""" -## @file async_io.py AsyncIO (with select_object or poll_object) +## @package PyHoot.async_io +# AsyncIO (with select_object or poll_object) +## @file async_io.py Implementation of @ref PyHoot.async_io from . import base, common_events, constants, poll_object, select_object diff --git a/base.py b/base.py index 07ddfe7..6b85c70 100644 --- a/base.py +++ b/base.py @@ -1,7 +1,6 @@ -"""Base module""" -## @file base.py Base module -# -*- coding: utf-8 -*- -# By Alon Bar-Lev alonbl +## @package PyHoot.base +# Base module +## @file base.py Implementation of @ref PyHoot.base import logging diff --git a/client.py b/client.py index 8af87c3..6d27e74 100644 --- a/client.py +++ b/client.py @@ -1,5 +1,7 @@ -"""@file client.py The client of the server""" -## @file client.py The client of the server +## @package PyHoot.client +# The client of the server +## @file client.py Implementation of @ref PyHoot.client + import Cookie import errno diff --git a/common_events.py b/common_events.py index aa0f7d9..f6b3068 100644 --- a/common_events.py +++ b/common_events.py @@ -1,5 +1,6 @@ -"""Events for async_io""" -## @file common_events.py Events for async_io +## @package PyHoot.common_events +# The client of the server +## @file common_events.py Implementation of @ref PyHoot.common_events import os import select diff --git a/compat.py b/compat.py index 4cbb25f..c64ebca 100644 --- a/compat.py +++ b/compat.py @@ -1,5 +1,6 @@ -"""Setting up os for the program""" -## @file compat.py Setting up os for the program +## @package PyHoot.compat +# Getting the OS ready +## @file compat.py Implementation of @ref PyHoot.compat import os diff --git a/constants.py b/constants.py index 1a8cdbf..3011edf 100644 --- a/constants.py +++ b/constants.py @@ -1,5 +1,6 @@ -"""Constants for the program""" -## @file constants.py Constants for the program +## @package PyHoot.constants +# Constants for the program +## @file constants.py Implementation of @ref PyHoot.constants ## Base for any HTTP page, you give it the title and the body of the page BASE_HTML = """ diff --git a/custom_exceptions.py b/custom_exceptions.py index 9f7b54e..8c35d35 100644 --- a/custom_exceptions.py +++ b/custom_exceptions.py @@ -1,5 +1,6 @@ -"""Special exceptions for the project""" -## @file custom_exceptions.py Special exceptions for the project +## @package PyHoot.custom_exceptions +# Special exceptions for the project +## @file custom_exceptions.py Implementation of @ref PyHoot.custom_exceptions class Disconnect(RuntimeError): diff --git a/docs/Guy Project File.pdf b/docs/Guy Project File.pdf index 17b0d43..2e4c0d5 100644 Binary files a/docs/Guy Project File.pdf and b/docs/Guy Project File.pdf differ diff --git a/file_object.py b/file_object.py index 2438873..6e387b5 100644 --- a/file_object.py +++ b/file_object.py @@ -1,8 +1,10 @@ -"""Object to handle reading from files -lkkkkkkkkkkkkkkkzssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss +## @package PyHoot.file_object +# Object to handle reading from files +## @file file_object.py Implementation of @ref PyHoot.file_object + +"""lkkkkkkkkkkkkkkkzssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss ssssssssssssssssssssssssssssssssssssssssssssssssssx Never let your cat document the code for you""" -## @file file_object.py Object to handle reading from files import os diff --git a/game.py b/game.py index 7d0aff5..d860d0e 100644 --- a/game.py +++ b/game.py @@ -1,5 +1,8 @@ -"""Game objects for the game, there is both player and master""" -## @file game.py Game objects for the game, there is both player and master +## @package PyHoot.game +# Game objects for the game, there is both player and master +## @file game.py Implementation of @ref PyHoot.game + + import base64 import os import random @@ -67,9 +70,6 @@ def __init__(self, quiz_name, common, base_directory): """name of the quiz""" self._quiz = quiz_name - """State of the master, will be used for encryption""" - self._state = "Registration" - """list of the players""" self._players_list = {} # {pid: {"player": GamePlayer, "_score":score} @@ -167,10 +167,11 @@ def get_question(self): def get_information(self): """Return the information about the question: It's name and how many questions""" - self._parser.get_information() + return self._parser.get_information() def move_to_next_question(self): - """Moving to the next question""" + """Moving to the next question. + """ self._parser.move_to_next_question() def get_left_questions(self): @@ -196,6 +197,22 @@ def get_answers(self): """Return the right answers as A,B,C,D""" return self._parser.get_question_answers() + def _get_picture(self): + """Get the name of the picture file in the qustion + @return Picture name (string) if available, else return None""" + question = ElementTree.fromstring( + self.get_question).find("./Text").text + if "img" not in question: + return None + question = question[question.index("") + len("/>")] + question = question[question.index("src=") + len("src=") + 1:] + question = question[:question.index('"')] + return question + + + + @property def join_number(self): """The number of the object in the database""" diff --git a/poll_object.py b/poll_object.py index 544f1bb..1f9d2af 100644 --- a/poll_object.py +++ b/poll_object.py @@ -1,5 +1,8 @@ -"""Select API for async_io.""" -## @file poll_object.py Select API for async_io. +## @package PyHoot.poll_object +# Select API for async_io. +## @file poll_object.py Implementation of @ref PyHoot.poll_object + + import select from . import base diff --git a/request.py b/request.py index c766722..2080410 100644 --- a/request.py +++ b/request.py @@ -1,5 +1,7 @@ -"""Request object to handle HTTP requests""" -## @file request.py Request object to handle HTTP requests +## @package PyHoot.request +# Request object to handle HTTP requests +## @file request.py Implementation of @ref PyHoot.request + import base diff --git a/select_object.py b/select_object.py index 2bff125..30c9490 100644 --- a/select_object.py +++ b/select_object.py @@ -1,5 +1,7 @@ -"""Select API for async_io.""" -## @file select_object.py Select API for async_io. +## @package PyHoot.select_object +# Select API for async_io. +## @file select_object.py Implementation of @ref PyHoot.select_object + import select diff --git a/server.py b/server.py index 777d466..5a4da10 100644 --- a/server.py +++ b/server.py @@ -1,5 +1,7 @@ -"""The server class""" -## @file server.py The server class +## @package PyHoot.server +# The server class +## @file server.py Implementation of @ref PyHoot.server + import errno import select diff --git a/services.py b/services.py index 00a8d26..cf1d6fb 100644 --- a/services.py +++ b/services.py @@ -1,3 +1,7 @@ +## @package PyHoot.services +# All the services get the method we need and return the data +## @file services.py Implementation of @ref PyHoot.services + """All the services get the method we need and return the data """ ## @file services.py All the services get the method we need and return diff --git a/util.py b/util.py index fb499bc..7633908 100644 --- a/util.py +++ b/util.py @@ -1,5 +1,8 @@ -"""Util file""" -## @file util.py Util file +## @package PyHoot.util +# Util file +## @file util.py Implementation of @ref PyHoot.util + + import httplib import mimetypes import socket diff --git a/xmlparser.py b/xmlparser.py index 46f165d..bb0eea9 100644 --- a/xmlparser.py +++ b/xmlparser.py @@ -1,5 +1,7 @@ -"""XML parser for test files""" -## @file xmlparser.py XML parser for test files +## @package PyHoot.xmlparser +# XML parser for test files +## @file xmlparser.py Implementation of @ref PyHoot.xmlparser + import os.path from xml.etree import ElementTree