scrapyloganalyzer 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. scrapyloganalyzer-0.0.1/LICENSE +29 -0
  2. scrapyloganalyzer-0.0.1/MANIFEST.in +9 -0
  3. scrapyloganalyzer-0.0.1/PKG-INFO +36 -0
  4. scrapyloganalyzer-0.0.1/README.rst +14 -0
  5. scrapyloganalyzer-0.0.1/docs/Makefile +20 -0
  6. scrapyloganalyzer-0.0.1/docs/api/index.rst +6 -0
  7. scrapyloganalyzer-0.0.1/docs/changelog.rst +7 -0
  8. scrapyloganalyzer-0.0.1/docs/conf.py +75 -0
  9. scrapyloganalyzer-0.0.1/docs/contributing/index.rst +12 -0
  10. scrapyloganalyzer-0.0.1/docs/index.rst +13 -0
  11. scrapyloganalyzer-0.0.1/docs/requirements.txt +1 -0
  12. scrapyloganalyzer-0.0.1/pyproject.toml +62 -0
  13. scrapyloganalyzer-0.0.1/scrapyloganalyzer/__init__.py +181 -0
  14. scrapyloganalyzer-0.0.1/scrapyloganalyzer.egg-info/PKG-INFO +36 -0
  15. scrapyloganalyzer-0.0.1/scrapyloganalyzer.egg-info/SOURCES.txt +29 -0
  16. scrapyloganalyzer-0.0.1/scrapyloganalyzer.egg-info/dependency_links.txt +1 -0
  17. scrapyloganalyzer-0.0.1/scrapyloganalyzer.egg-info/requires.txt +5 -0
  18. scrapyloganalyzer-0.0.1/scrapyloganalyzer.egg-info/top_level.txt +3 -0
  19. scrapyloganalyzer-0.0.1/setup.cfg +4 -0
  20. scrapyloganalyzer-0.0.1/tests/__init__.py +5 -0
  21. scrapyloganalyzer-0.0.1/tests/fixtures/log1.log +87 -0
  22. scrapyloganalyzer-0.0.1/tests/fixtures/log_crawl_time_crawl_statistic.log +3 -0
  23. scrapyloganalyzer-0.0.1/tests/fixtures/log_crawl_time_log_message.log +1 -0
  24. scrapyloganalyzer-0.0.1/tests/fixtures/log_crawl_time_spider_argument.log +3 -0
  25. scrapyloganalyzer-0.0.1/tests/fixtures/log_error1.log +120 -0
  26. scrapyloganalyzer-0.0.1/tests/fixtures/log_error_invalid_json.log +117 -0
  27. scrapyloganalyzer-0.0.1/tests/fixtures/log_from_date1.log +161 -0
  28. scrapyloganalyzer-0.0.1/tests/fixtures/log_in_progress1.log +68 -0
  29. scrapyloganalyzer-0.0.1/tests/fixtures/log_sample1.log +105 -0
  30. scrapyloganalyzer-0.0.1/tests/fixtures/log_sigint1.log +75 -0
  31. scrapyloganalyzer-0.0.1/tests/test_scrapy_log_file.py +209 -0
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2022, Open Contracting Partnership
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,9 @@
1
+ include LICENSE
2
+ recursive-include docs *.py
3
+ recursive-include docs *.rst
4
+ recursive-include docs *.txt
5
+ recursive-include docs Makefile
6
+ recursive-include tests *.log
7
+ recursive-include tests *.py
8
+ exclude .pre-commit-config.yaml
9
+ exclude .readthedocs.yaml
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: scrapyloganalyzer
3
+ Version: 0.0.1
4
+ Summary: Provides methods to analyze the quality of a Scrapy crawl.
5
+ Author-email: Open Contracting Partnership <data@open-contracting.org>
6
+ License: BSD
7
+ Project-URL: Homepage, https://github.com/open-contracting/scrapy-log-analyzer
8
+ Classifier: License :: OSI Approved :: BSD License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: Implementation :: CPython
15
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
16
+ Description-Content-Type: text/x-rst
17
+ License-File: LICENSE
18
+ Requires-Dist: logparser
19
+ Provides-Extra: test
20
+ Requires-Dist: coverage; extra == "test"
21
+ Requires-Dist: pytest; extra == "test"
22
+
23
+ |PyPI Version| |Build Status| |Coverage Status| |Python Version|
24
+
25
+ Provides methods to analyze the quality of a Scrapy crawl.
26
+
27
+ (If you are viewing this on GitHub, open the `full documentation <https://scrapy-log-analyzer.readthedocs.io/>`__ for additional details.)
28
+
29
+ .. |PyPI Version| image:: https://img.shields.io/pypi/v/scrapyloganalyzer.svg
30
+ :target: https://pypi.org/project/scrapyloganalyzer/
31
+ .. |Build Status| image:: https://github.com/open-contracting/scrapy-log-analyzer/actions/workflows/ci.yml/badge.svg
32
+ :target: https://github.com/open-contracting/scrapy-log-analyzer/actions/workflows/ci.yml
33
+ .. |Coverage Status| image:: https://coveralls.io/repos/github/open-contracting/scrapy-log-analyzer/badge.svg?branch=main
34
+ :target: https://coveralls.io/github/open-contracting/scrapy-log-analyzer?branch=main
35
+ .. |Python Version| image:: https://img.shields.io/pypi/pyversions/scrapyloganalyzer.svg
36
+ :target: https://pypi.org/project/scrapyloganalyzer/
@@ -0,0 +1,14 @@
1
+ |PyPI Version| |Build Status| |Coverage Status| |Python Version|
2
+
3
+ Provides methods to analyze the quality of a Scrapy crawl.
4
+
5
+ (If you are viewing this on GitHub, open the `full documentation <https://scrapy-log-analyzer.readthedocs.io/>`__ for additional details.)
6
+
7
+ .. |PyPI Version| image:: https://img.shields.io/pypi/v/scrapyloganalyzer.svg
8
+ :target: https://pypi.org/project/scrapyloganalyzer/
9
+ .. |Build Status| image:: https://github.com/open-contracting/scrapy-log-analyzer/actions/workflows/ci.yml/badge.svg
10
+ :target: https://github.com/open-contracting/scrapy-log-analyzer/actions/workflows/ci.yml
11
+ .. |Coverage Status| image:: https://coveralls.io/repos/github/open-contracting/scrapy-log-analyzer/badge.svg?branch=main
12
+ :target: https://coveralls.io/github/open-contracting/scrapy-log-analyzer?branch=main
13
+ .. |Python Version| image:: https://img.shields.io/pypi/pyversions/scrapyloganalyzer.svg
14
+ :target: https://pypi.org/project/scrapyloganalyzer/
@@ -0,0 +1,20 @@
1
+ # Minimal makefile for Sphinx documentation
2
+ #
3
+
4
+ # You can set these variables from the command line, and also
5
+ # from the environment for the first two.
6
+ SPHINXOPTS ?=
7
+ SPHINXBUILD ?= sphinx-build
8
+ SOURCEDIR = .
9
+ BUILDDIR = _build
10
+
11
+ # Put it first so that "make" without argument is like "make help".
12
+ help:
13
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
+
15
+ .PHONY: help Makefile
16
+
17
+ # Catch-all target: route all unknown targets to Sphinx using the new
18
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19
+ %: Makefile
20
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@@ -0,0 +1,6 @@
1
+ API reference
2
+ =============
3
+
4
+ .. automodule:: scrapyloganalyzer
5
+ :members:
6
+ :undoc-members:
@@ -0,0 +1,7 @@
1
+ Changelog
2
+ =========
3
+
4
+ 0.0.1 (2024-12-13)
5
+ ------------------
6
+
7
+ First release.
@@ -0,0 +1,75 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # This file only contains a selection of the most common options. For a full
4
+ # list see the documentation:
5
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
6
+
7
+ # -- Path setup --------------------------------------------------------------
8
+
9
+ # If extensions (or modules to document with autodoc) are in another directory,
10
+ # add these directories to sys.path here. If the directory is relative to the
11
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
12
+
13
+ import os
14
+ import sys
15
+
16
+ sys.path.insert(0, os.path.abspath(".."))
17
+
18
+
19
+ # -- Project information -----------------------------------------------------
20
+
21
+ project = "Scrapy Log Analyzer"
22
+ copyright = "2022, Open Contracting Partnership"
23
+ author = "Open Contracting Partnership"
24
+
25
+ # The short X.Y version
26
+ version = "0.0.1"
27
+ # The full version, including alpha/beta/rc tags
28
+ release = version
29
+
30
+
31
+ # -- General configuration ---------------------------------------------------
32
+
33
+ # Add any Sphinx extension module names here, as strings. They can be
34
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
35
+ # ones.
36
+ extensions = [
37
+ "sphinx.ext.autodoc",
38
+ "sphinx.ext.intersphinx",
39
+ "sphinx.ext.viewcode",
40
+ ]
41
+
42
+ # Add any paths that contain templates here, relative to this directory.
43
+ templates_path = ["_templates"]
44
+
45
+ # List of patterns, relative to source directory, that match files and
46
+ # directories to ignore when looking for source files.
47
+ # This pattern also affects html_static_path and html_extra_path.
48
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
49
+
50
+
51
+ # -- Options for HTML output -------------------------------------------------
52
+
53
+ # The theme to use for HTML and HTML Help pages. See the documentation for
54
+ # a list of builtin themes.
55
+ #
56
+ html_theme = "furo"
57
+
58
+ # Add any paths that contain custom static files (such as style sheets) here,
59
+ # relative to this directory. They are copied after the builtin static files,
60
+ # so a file named "default.css" will overwrite the builtin "default.css".
61
+ html_static_path = []
62
+
63
+
64
+ # -- Extension configuration -------------------------------------------------
65
+
66
+ autodoc_default_options = {
67
+ "members": None,
68
+ "member-order": "bysource",
69
+ }
70
+ autodoc_typehints = "description"
71
+ autodoc_type_aliases = {}
72
+
73
+ intersphinx_mapping = {
74
+ "python": ("https://docs.python.org/3", None),
75
+ }
@@ -0,0 +1,12 @@
1
+ Contributing
2
+ ============
3
+
4
+ Setup
5
+ -----
6
+
7
+ Set up the git pre-commit hook:
8
+
9
+ .. code-block:: bash
10
+
11
+ pip install pre-commit
12
+ pre-commit install
@@ -0,0 +1,13 @@
1
+ Scrapy Log Analyzer |release|
2
+ =============================
3
+
4
+ .. include:: ../README.rst
5
+
6
+ .. toctree::
7
+ :caption: Contents
8
+
9
+ api/index
10
+ contributing/index
11
+ changelog
12
+
13
+ Copyright (c) 2022 Open Contracting Partnership, released under the BSD license
@@ -0,0 +1 @@
1
+ furo
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "scrapyloganalyzer"
7
+ version = "0.0.1"
8
+ authors = [{name = "Open Contracting Partnership", email = "data@open-contracting.org"}]
9
+ description = "Provides methods to analyze the quality of a Scrapy crawl."
10
+ readme = "README.rst"
11
+ license = {text = "BSD"}
12
+ urls = {Homepage = "https://github.com/open-contracting/scrapy-log-analyzer"}
13
+ classifiers = [
14
+ "License :: OSI Approved :: BSD License",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: Implementation :: CPython",
21
+ "Programming Language :: Python :: Implementation :: PyPy",
22
+ ]
23
+ dependencies = [
24
+ "logparser",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ test = [
29
+ "coverage",
30
+ "pytest",
31
+ ]
32
+
33
+ [tool.setuptools.packages.find]
34
+ exclude = [
35
+ "tests",
36
+ "tests.*",
37
+ ]
38
+
39
+ [tool.ruff]
40
+ line-length = 119
41
+ target-version = "py39"
42
+
43
+ [tool.ruff.lint]
44
+ select = ["ALL"]
45
+ ignore = [
46
+ "ANN", "C901", "COM812", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000",
47
+ "D104", "D205",
48
+ "DTZ",
49
+ "PTH",
50
+ ]
51
+
52
+ [tool.ruff.lint.flake8-builtins]
53
+ builtins-ignorelist = ["copyright"]
54
+
55
+ [tool.ruff.lint.flake8-unused-arguments]
56
+ ignore-variadic-names = true
57
+
58
+ [tool.ruff.lint.per-file-ignores]
59
+ "docs/conf.py" = ["D100", "INP001"]
60
+ "tests/*" = [
61
+ "ARG001", "D", "FBT003", "INP001", "PLR2004", "S", "TRY003",
62
+ ]
@@ -0,0 +1,181 @@
1
+ import ast
2
+ import datetime
3
+ import os
4
+ import re
5
+ from collections import defaultdict
6
+
7
+ from logparser import parse
8
+ from logparser.common import DATETIME_PATTERN, Common
9
+
10
+ # Kingfisher Collect logs an INFO message starting with "Spider arguments:".
11
+ SPIDER_ARGUMENTS_SEARCH_STRING = " INFO: Spider arguments: "
12
+
13
+ MAXIMUM_TIMEDELTA = 3
14
+
15
+ # Hotfix: https://github.com/my8100/logparser/pull/19
16
+ Common.SIGTERM_PATTERN = re.compile(r"^%s[ ].+?:[ ](Received[ ]SIG(?:BREAK|INT|TERM)([ ]twice)?)," % DATETIME_PATTERN) # noqa: UP031
17
+
18
+
19
+ class ScrapyLogFile:
20
+ """A representation of a Scrapy log file."""
21
+
22
+ @classmethod
23
+ def find(cls, logs_directory, source_id, data_version):
24
+ """
25
+ Find and return the first matching log file for the given crawl.
26
+
27
+ :param str logs_directory: Kingfisher Collect's project directory within Scrapyd's logs_dir directory
28
+ :param str source_id: the spider's name
29
+ :param datetime.datetime data_version: the crawl directory's name, parsed as a datetime
30
+ """
31
+ source_directory = os.path.join(logs_directory, source_id)
32
+ if os.path.isdir(source_directory):
33
+ with os.scandir(source_directory) as it:
34
+ for entry in it:
35
+ if entry.name.endswith(".log"):
36
+ scrapy_log_file = ScrapyLogFile(entry.path)
37
+ if scrapy_log_file.match(data_version):
38
+ return scrapy_log_file
39
+ return None
40
+
41
+ def __init__(self, name) -> None:
42
+ """:param str name: the full path to the log file"""
43
+ self.name = name
44
+
45
+ self._logparser = None
46
+ self._item_counts = None
47
+ self._spider_arguments = None
48
+
49
+ def delete(self):
50
+ """Delete the log file and any log summary ending in ``.stats``."""
51
+ if os.path.isfile(self.name):
52
+ os.remove(self.name)
53
+ summary = f"{self.name}.stats"
54
+ if os.path.isfile(summary):
55
+ os.remove(summary)
56
+
57
+ # Logparser processing
58
+
59
+ @property
60
+ def logparser(self) -> dict:
61
+ """Return the output of `logparser <https://pypi.org/project/logparser/>`__."""
62
+ if self._logparser is None:
63
+ with open(self.name) as f:
64
+ # `taillines=0` sets the 'tail' key to all lines, so we set it to 1.
65
+ self._logparser = parse(f.read(), headlines=0, taillines=1)
66
+
67
+ return self._logparser
68
+
69
+ def match(self, data_version) -> bool:
70
+ """
71
+ Return whether the crawl directory's name, parsed as a datetime, is less than 3 seconds after the log file's
72
+ start time.
73
+ """
74
+ return 0 <= data_version.timestamp() - self.crawl_time.timestamp() < MAXIMUM_TIMEDELTA
75
+
76
+ @property
77
+ def crawl_time(self) -> datetime.datetime:
78
+ """
79
+ Return the ``crawl_time`` spider argument if set, or the ``start_time`` crawl statistic otherwise. If neither
80
+ is logged, return the time of the first log message.
81
+ """
82
+ crawl_time = self.spider_arguments.get("crawl_time")
83
+ if crawl_time:
84
+ return datetime.datetime.strptime(crawl_time, "%Y-%m-%dT%H:%M:%S")
85
+ if "start_time" in self.logparser["crawler_stats"]:
86
+ return eval(self.logparser["crawler_stats"]["start_time"]).replace(microsecond=0) # noqa: S307
87
+ return datetime.datetime.fromtimestamp(self.logparser["first_log_timestamp"])
88
+
89
+ def is_finished(self) -> bool:
90
+ """
91
+ Return whether the log file contains a "Spider closed (finished)" log message or a ``finish_reason`` crawl
92
+ statistic set to "finished".
93
+ """
94
+ # See https://kingfisher-collect.readthedocs.io/en/latest/logs.html#check-the-reason-for-closing-the-spider
95
+ # logparser's `finish_reason` is "N/A" for an unclean shutdown, because crawl statistics aren't logged.
96
+ return self.logparser["finish_reason"] == "finished"
97
+
98
+ # Line-by-line processing
99
+
100
+ @property
101
+ def item_counts(self) -> dict:
102
+ """Return the number of each type of item, according to the log file."""
103
+ if self._item_counts is None:
104
+ self._process_line_by_line()
105
+
106
+ return self._item_counts
107
+
108
+ @property
109
+ def spider_arguments(self) -> dict:
110
+ """Return the spider's arguments."""
111
+ if self._spider_arguments is None:
112
+ self._process_line_by_line()
113
+
114
+ return self._spider_arguments
115
+
116
+ def is_complete(self) -> bool:
117
+ """Return whether the crawl collected a subset of the dataset, according to the log file."""
118
+ # See https://kingfisher-collect.readthedocs.io/en/latest/spiders.html#spider-arguments
119
+ return not any(
120
+ self.spider_arguments.get(arg)
121
+ for arg in (
122
+ "from_date",
123
+ "until_date",
124
+ "portal",
125
+ "publisher",
126
+ "system",
127
+ "sample",
128
+ "path",
129
+ "qs:",
130
+ )
131
+ )
132
+
133
+ def _process_line_by_line(self) -> None:
134
+ self._item_counts = defaultdict(int)
135
+ self._spider_arguments = {}
136
+
137
+ buf = []
138
+ with open(self.name) as f:
139
+ for line in f:
140
+ if buf or line.startswith("{"):
141
+ buf.append(line.rstrip())
142
+ if buf and buf[-1].endswith("}"):
143
+ try:
144
+ # Scrapy logs items as dicts. FileError items, representing retrieval errors, are identified by
145
+ # an 'errors' key. FileError items use only simple types, so `ast.literal_eval` can be used.
146
+ item = ast.literal_eval("".join(buf))
147
+ if "errors" in item:
148
+ self._item_counts["FileError"] += 1
149
+ elif "number" in item:
150
+ self._item_counts["FileItem"] += 1
151
+ elif "data_type" in item:
152
+ self._item_counts["File"] += 1
153
+ except ValueError:
154
+ # Scrapy dumps stats as a dict, which uses `datetime.datetime` types that can't be parsed with
155
+ # `ast.literal_eval`.
156
+ pass
157
+ buf = []
158
+
159
+ index = line.find(SPIDER_ARGUMENTS_SEARCH_STRING)
160
+ if index > -1:
161
+ # `eval` is used, because the string can contain `datetime.date` and is written by trusted code in
162
+ # Kingfisher Collect. Otherwise, we can modify the string so that `ast.literal_eval` can be used.
163
+ self._spider_arguments = eval(line[index + len(SPIDER_ARGUMENTS_SEARCH_STRING) :]) # noqa: S307
164
+
165
+ # Mixed processing
166
+
167
+ @property
168
+ def error_rate(self) -> float:
169
+ """
170
+ Return an estimated lower bound of the true error rate.
171
+
172
+ Kingfisher Collect is expected to yield at most one FileError item per request leading to a File item, so the
173
+ true error rate can only be less than this estimated lower bound if Kingfisher Collect breaks this expectation.
174
+ On the other hand, the true error rate can easily be higher than the estimated lower bound; for example:
175
+
176
+ - If the spider crawls 10 URLs, each returning 99 URLs, each returning OCDS data, and the requests for 5 of
177
+ the 10 fail, then the estimated lower bound is 5 / 500 (1%), though the true error rate is 50%.
178
+ - Similarly if the spider crawls 10 archive files, each containing 99 OCDS files.
179
+ """
180
+ error_count = self.item_counts["FileError"] + self.logparser["crawler_stats"].get("invalid_json_count", 0)
181
+ return error_count / (self.item_counts["File"] + error_count)
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.1
2
+ Name: scrapyloganalyzer
3
+ Version: 0.0.1
4
+ Summary: Provides methods to analyze the quality of a Scrapy crawl.
5
+ Author-email: Open Contracting Partnership <data@open-contracting.org>
6
+ License: BSD
7
+ Project-URL: Homepage, https://github.com/open-contracting/scrapy-log-analyzer
8
+ Classifier: License :: OSI Approved :: BSD License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: Implementation :: CPython
15
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
16
+ Description-Content-Type: text/x-rst
17
+ License-File: LICENSE
18
+ Requires-Dist: logparser
19
+ Provides-Extra: test
20
+ Requires-Dist: coverage; extra == "test"
21
+ Requires-Dist: pytest; extra == "test"
22
+
23
+ |PyPI Version| |Build Status| |Coverage Status| |Python Version|
24
+
25
+ Provides methods to analyze the quality of a Scrapy crawl.
26
+
27
+ (If you are viewing this on GitHub, open the `full documentation <https://scrapy-log-analyzer.readthedocs.io/>`__ for additional details.)
28
+
29
+ .. |PyPI Version| image:: https://img.shields.io/pypi/v/scrapyloganalyzer.svg
30
+ :target: https://pypi.org/project/scrapyloganalyzer/
31
+ .. |Build Status| image:: https://github.com/open-contracting/scrapy-log-analyzer/actions/workflows/ci.yml/badge.svg
32
+ :target: https://github.com/open-contracting/scrapy-log-analyzer/actions/workflows/ci.yml
33
+ .. |Coverage Status| image:: https://coveralls.io/repos/github/open-contracting/scrapy-log-analyzer/badge.svg?branch=main
34
+ :target: https://coveralls.io/github/open-contracting/scrapy-log-analyzer?branch=main
35
+ .. |Python Version| image:: https://img.shields.io/pypi/pyversions/scrapyloganalyzer.svg
36
+ :target: https://pypi.org/project/scrapyloganalyzer/
@@ -0,0 +1,29 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.rst
4
+ pyproject.toml
5
+ docs/Makefile
6
+ docs/changelog.rst
7
+ docs/conf.py
8
+ docs/index.rst
9
+ docs/requirements.txt
10
+ docs/api/index.rst
11
+ docs/contributing/index.rst
12
+ scrapyloganalyzer/__init__.py
13
+ scrapyloganalyzer.egg-info/PKG-INFO
14
+ scrapyloganalyzer.egg-info/SOURCES.txt
15
+ scrapyloganalyzer.egg-info/dependency_links.txt
16
+ scrapyloganalyzer.egg-info/requires.txt
17
+ scrapyloganalyzer.egg-info/top_level.txt
18
+ tests/__init__.py
19
+ tests/test_scrapy_log_file.py
20
+ tests/fixtures/log1.log
21
+ tests/fixtures/log_crawl_time_crawl_statistic.log
22
+ tests/fixtures/log_crawl_time_log_message.log
23
+ tests/fixtures/log_crawl_time_spider_argument.log
24
+ tests/fixtures/log_error1.log
25
+ tests/fixtures/log_error_invalid_json.log
26
+ tests/fixtures/log_from_date1.log
27
+ tests/fixtures/log_in_progress1.log
28
+ tests/fixtures/log_sample1.log
29
+ tests/fixtures/log_sigint1.log
@@ -0,0 +1,5 @@
1
+ logparser
2
+
3
+ [test]
4
+ coverage
5
+ pytest
@@ -0,0 +1,3 @@
1
+ dist
2
+ docs
3
+ scrapyloganalyzer
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ import os.path
2
+
3
+
4
+ def path(filename):
5
+ return os.path.join("tests", "fixtures", filename)