+ msg = f"Successful downloads from the sources:\n"
+ for source in self._cfg.environment[u"data-sources"]:
+ if source[u"successful-downloads"]:
+ msg += (
+ f"{source[u'url']}/{source[u'path']}/"
+ f"{source[u'file-name']}: "
+ f"{source[u'successful-downloads']}\n"
+ )
+ logging.info(msg)
+
+ def process_local_file(self, local_file, job=u"local", build_nr=1,
+ replace=True):
+ """Process local XML file given as a command-line parameter.
+
+ :param local_file: The file to process.
+ :param job: Job name.
+ :param build_nr: Build number.
+ :param replace: If True, the information about jobs and builds is
+ replaced by the new one, otherwise the new jobs and builds are
+ added.
+ :type local_file: str
+ :type job: str
+ :type build_nr: int
+ :type replace: bool
+ :raises: PresentationError if an error occurs.
+ """
+ if not isfile(local_file):
+ raise PresentationError(f"The file {local_file} does not exist.")
+
+ try:
+ build_nr = int(local_file.split(u"/")[-1].split(u".")[0])
+ except (IndexError, ValueError):
+ pass
+
+ build = {
+ u"build": build_nr,
+ u"status": u"failed",
+ u"file-name": local_file
+ }
+ if replace:
+ self._cfg.input = dict()
+ self._cfg.add_build(job, build)
+
+ logging.info(f"Processing {job}: {build_nr:2d}: {local_file}")
+ data = self._parse_tests(job, build)
+ if data is None:
+ raise PresentationError(
+ f"Error occurred while parsing the file {local_file}"
+ )
+
+ build_data = pd.Series({
+ u"metadata": pd.Series(
+ list(data[u"metadata"].values()),
+ index=list(data[u"metadata"].keys())
+ ),
+ u"suites": pd.Series(
+ list(data[u"suites"].values()),
+ index=list(data[u"suites"].keys())
+ ),
+ u"tests": pd.Series(
+ list(data[u"tests"].values()),
+ index=list(data[u"tests"].keys())
+ )
+ })
+
+ if self._input_data.get(job, None) is None:
+ self._input_data[job] = pd.Series(dtype="float64")
+ self._input_data[job][str(build_nr)] = build_data
+
+ self._cfg.set_input_state(job, build_nr, u"processed")
+
+ def process_local_directory(self, local_dir, replace=True):
+ """Process local directory with XML file(s). The directory is processed
+ as a 'job' and the XML files in it as builds.
+ If the given directory contains only sub-directories, these
+ sub-directories processed as jobs and corresponding XML files as builds
+ of their job.
+
+ :param local_dir: Local directory to process.
+ :param replace: If True, the information about jobs and builds is
+ replaced by the new one, otherwise the new jobs and builds are
+ added.
+ :type local_dir: str
+ :type replace: bool
+ """
+ if not isdir(local_dir):
+ raise PresentationError(
+ f"The directory {local_dir} does not exist."
+ )
+
+ # Check if the given directory includes only files, or only directories
+ _, dirnames, filenames = next(walk(local_dir))
+
+ if filenames and not dirnames:
+ filenames.sort()
+ # local_builds:
+ # key: dir (job) name, value: list of file names (builds)
+ local_builds = {
+ local_dir: [join(local_dir, name) for name in filenames]
+ }
+
+ elif dirnames and not filenames:
+ dirnames.sort()
+ # local_builds:
+ # key: dir (job) name, value: list of file names (builds)
+ local_builds = dict()
+ for dirname in dirnames:
+ builds = [
+ join(local_dir, dirname, name)
+ for name in listdir(join(local_dir, dirname))
+ if isfile(join(local_dir, dirname, name))
+ ]
+ if builds:
+ local_builds[dirname] = sorted(builds)
+
+ elif not filenames and not dirnames:
+ raise PresentationError(f"The directory {local_dir} is empty.")
+ else:
+ raise PresentationError(
+ f"The directory {local_dir} can include only files or only "
+ f"directories, not both.\nThe directory {local_dir} includes "
+ f"file(s):\n{filenames}\nand directories:\n{dirnames}"
+ )
+
+ if replace:
+ self._cfg.input = dict()
+
+ for job, files in local_builds.items():
+ for idx, local_file in enumerate(files):
+ self.process_local_file(local_file, job, idx + 1, replace=False)
+