├── .bumpversion.cfg ├── .coveragerc ├── .flake8 ├── .gitchangelog.rc ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── AUTHORS.md ├── CHANGELOG.rst ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── django_check_seo ├── __init__.py ├── apps.py ├── checks │ ├── __init__.py │ ├── custom_list.py │ └── site.py ├── checks_list │ ├── __init__.py │ ├── check_description.py │ ├── check_h1.py │ ├── check_h2.py │ ├── check_images.py │ ├── check_keyword_url.py │ ├── check_keywords.py │ ├── check_links.py │ ├── check_title.py │ ├── check_url.py │ ├── content_words_number.py │ ├── keyword_present_first_paragraph.py │ └── launch_checks.py ├── cms_toolbars.py ├── conf │ ├── __init__.py │ └── settings.py ├── locale │ └── fr │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── static │ └── django-check-seo │ │ ├── design.css │ │ ├── fonts │ │ ├── LICENCE │ │ ├── NOTICE │ │ ├── Roboto-Black.ttf │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-Regular.ttf │ │ └── Roboto-Thin.ttf │ │ ├── logo-small.png │ │ └── logo.png ├── templates │ └── django_check_seo │ │ ├── default.html │ │ └── element.html ├── urls.py └── views.py ├── launch_tests.sh ├── pytest.ini ├── setup.cfg ├── setup.py ├── tests ├── test_content_words_number.py ├── test_description.py ├── test_h1.py ├── test_h2.py ├── test_images.py ├── test_keyword_present_first_paragraph.py ├── test_keyword_url.py ├── test_keywords.py ├── test_links.py ├── test_title.py └── test_url.py └── tests_settings.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.0.1 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.cfg] 7 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = django_check_seo/checks/* 3 | django_check_seo/conf/* 4 | */__init__.py 5 | django_check_seo/urls.py 6 | django_check_seo/urls.py 7 | django_check_seo/views.py 8 | django_check_seo/check_list/launch_checks.py 9 | django_check_seo/apps.py 10 | django_check_seo/cms_toolbars.py 11 | django_check_seo/checks_list/launch_checks.py 12 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E501, E203, W503 3 | exclude = .git,__pycache__,build,dist, 4 | -------------------------------------------------------------------------------- /.gitchangelog.rc: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; mode: python -*- 2 | ## 3 | ## Format 4 | ## 5 | ## ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...] 6 | ## 7 | ## Description 8 | ## 9 | ## ACTION is one of 'chg', 'fix', 'new' 10 | ## 11 | ## Is WHAT the change is about. 12 | ## 13 | ## 'chg' is for refactor, small improvement, cosmetic changes... 14 | ## 'fix' is for bug fixes 15 | ## 'new' is for new features, big improvement 16 | ## 17 | ## AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc' 18 | ## 19 | ## Is WHO is concerned by the change. 20 | ## 21 | ## 'dev' is for developpers (API changes, refactors...) 22 | ## 'usr' is for final users (UI changes) 23 | ## 'pkg' is for packagers (packaging changes) 24 | ## 'test' is for testers (test only related changes) 25 | ## 'doc' is for doc guys (doc only changes) 26 | ## 27 | ## COMMIT_MSG is ... well ... the commit message itself. 28 | ## 29 | ## TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic' 30 | ## 31 | ## They are preceded with a '!' or a '@' (prefer the former, as the 32 | ## latter is wrongly interpreted in github.) Commonly used tags are: 33 | ## 34 | ## 'refactor' is obviously for refactoring code only 35 | ## 'minor' is for a very meaningless change (a typo, adding a comment) 36 | ## 'cosmetic' is for cosmetic driven change (re-indentation, 80-col...) 37 | ## 'wip' is for partial functionality but complete subfunctionality. 38 | ## 39 | ## Example: 40 | ## 41 | ## new: usr: support of bazaar implemented 42 | ## chg: re-indentend some lines !cosmetic 43 | ## new: dev: updated code to be compatible with last version of killer lib. 44 | ## fix: pkg: updated year of licence coverage. 45 | ## new: test: added a bunch of test around user usability of feature X. 46 | ## fix: typo in spelling my name in comment. !minor 47 | ## 48 | ## Please note that multi-line commit message are supported, and only the 49 | ## first line will be considered as the "summary" of the commit message. So 50 | ## tags, and other rules only applies to the summary. The body of the commit 51 | ## message will be displayed in the changelog without reformatting. 52 | 53 | 54 | ## 55 | ## ``ignore_regexps`` is a line of regexps 56 | ## 57 | ## Any commit having its full commit message matching any regexp listed here 58 | ## will be ignored and won't be reported in the changelog. 59 | ## 60 | ignore_regexps = [ 61 | r'^Bump version', 62 | r'^Merge', 63 | r'^Added tag', 64 | r'^$', ## ignore commits with empty messages 65 | ] 66 | 67 | 68 | ## ``section_regexps`` is a list of 2-tuples associating a string label and a 69 | ## list of regexp 70 | ## 71 | ## Commit messages will be classified in sections thanks to this. Section 72 | ## titles are the label, and a commit is classified under this section if any 73 | ## of the regexps associated is matching. 74 | ## 75 | ## Please note that ``section_regexps`` will only classify commits and won't 76 | ## make any changes to the contents. So you'll probably want to go check 77 | ## ``subject_process`` (or ``body_process``) to do some changes to the subject, 78 | ## whenever you are tweaking this variable. 79 | ## 80 | scop_regex = r'([\(|\[].*[\)|\]])' 81 | 82 | section_regexps = [ 83 | ('Breaking changes', [ 84 | r'(?i)^(BREAK)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 85 | ]), 86 | ('Features', [ 87 | r'(?i)^(FEAT)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 88 | ]), 89 | ('Bug fixes', [ 90 | r'(?i)^(FIX)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 91 | ]), 92 | ('Documentation', [ 93 | r'(?i)^(DOCS?)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 94 | ]), 95 | ('Maintenance', [ 96 | r'(?i)^(MAINT)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 97 | ]), 98 | ('Format', [ 99 | r'(?i)^(FORMAT)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 100 | ]), 101 | ('Tests', [ 102 | r'(?i)^(TEST)\s*' + scop_regex + '?\s*:\s*([^\n]*)$', 103 | ]), 104 | ('Other', None ## Match all lines 105 | ), 106 | ] 107 | 108 | 109 | ## ``body_process`` is a callable 110 | ## 111 | ## This callable will be given the original body and result will 112 | ## be used in the changelog. 113 | ## 114 | ## Available constructs are: 115 | ## 116 | ## - any python callable that take one txt argument and return txt argument. 117 | ## 118 | ## - ReSub(pattern, replacement): will apply regexp substitution. 119 | ## 120 | ## - Indent(chars=" "): will indent the text with the prefix 121 | ## Please remember that template engines gets also to modify the text and 122 | ## will usually indent themselves the text if needed. 123 | ## 124 | ## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns 125 | ## 126 | ## - noop: do nothing 127 | ## 128 | ## - ucfirst: ensure the first letter is uppercase. 129 | ## (usually used in the ``subject_process`` pipeline) 130 | ## 131 | ## - final_dot: ensure text finishes with a dot 132 | ## (usually used in the ``subject_process`` pipeline) 133 | ## 134 | ## - strip: remove any spaces before or after the content of the string 135 | ## 136 | ## - SetIfEmpty(msg="No commit message."): will set the text to 137 | ## whatever given ``msg`` if the current text is empty. 138 | ## 139 | ## Additionally, you can `pipe` the provided filters, for instance: 140 | body_process = strip | ReSub(r'(.$)', '\n') 141 | 142 | 143 | ## ``subject_process`` is a callable 144 | ## 145 | ## This callable will be given the original subject and result will 146 | ## be used in the changelog. 147 | ## 148 | ## Available constructs are those listed in ``body_process`` doc. 149 | subject_process = (strip | 150 | ReSub(r'(?i)^(FIX|FEAT|DOCS?|MAINT|BREAK|FORMAT|TEST)\s*' + scop_regex + '?\s*:?\s*([^\n@]*)(@[a-z]+\s+)*$', r'\3') | 151 | SetIfEmpty("No summary") | ucfirst) 152 | 153 | 154 | ## ``tag_filter_regexp`` is a regexp 155 | ## 156 | ## Tags that will be used for the changelog must match this regexp. 157 | ## 158 | tag_filter_regexp = r'^v?[0-9]+\.[0-9]+(\.[0-9]+)?$' 159 | 160 | 161 | ## ``unreleased_version_label`` is a string or a callable that outputs a string 162 | ## 163 | ## This label will be used as the changelog Title of the last set of changes 164 | ## between last valid tag and HEAD if any. 165 | unreleased_version_label = "(unreleased)" 166 | 167 | 168 | ## ``output_engine`` is a callable 169 | ## 170 | ## This will change the output format of the generated changelog file 171 | ## 172 | ## Available choices are: 173 | ## 174 | ## - rest_py 175 | ## 176 | ## Legacy pure python engine, outputs ReSTructured text. 177 | ## This is the default. 178 | ## 179 | ## - mustache() 180 | ## 181 | ## Template name could be any of the available templates in 182 | ## ``templates/mustache/*.tpl``. 183 | ## Requires python package ``pystache``. 184 | ## Examples: 185 | ## - mustache("markdown") 186 | ## - mustache("restructuredtext") 187 | ## 188 | ## - makotemplate() 189 | ## 190 | ## Template name could be any of the available templates in 191 | ## ``templates/mako/*.tpl``. 192 | ## Requires python package ``mako``. 193 | ## Examples: 194 | ## - makotemplate("restructuredtext") 195 | ## 196 | output_engine = rest_py 197 | 198 | 199 | ## ``include_merge`` is a boolean 200 | ## 201 | ## This option tells git-log whether to include merge commits in the log. 202 | ## The default is to include them. 203 | include_merge = False 204 | 205 | 206 | ## ``log_encoding`` is a string identifier 207 | ## 208 | ## This option tells gitchangelog what encoding is outputed by ``git log``. 209 | ## The default is to be clever about it: it checks ``git config`` for 210 | ## ``i18n.logOutputEncoding``, and if not found will default to git's own 211 | ## default: ``utf-8``. 212 | #log_encoding = 'utf-8' 213 | 214 | 215 | ## ``publish`` is a callable 216 | ## 217 | ## Sets what ``gitchangelog`` should do with the output generated by 218 | ## the output engine. ``publish`` is a callable taking one argument 219 | ## that is an interator on lines from the output engine. 220 | ## 221 | ## Some helper callable are provided: 222 | ## 223 | ## Available choices are: 224 | ## 225 | ## - stdout 226 | ## 227 | ## Outputs directly to standard output 228 | ## (This is the default) 229 | ## 230 | ## - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start()) 231 | ## 232 | ## Creates a callable that will parse given file for the given 233 | ## regex pattern and will insert the output in the file. 234 | ## ``idx`` is a callable that receive the matching object and 235 | ## must return a integer index point where to insert the 236 | ## the output in the file. Default is to return the position of 237 | ## the start of the matched string. 238 | ## 239 | ## - FileRegexSubst(file, pattern, replace, flags) 240 | ## 241 | ## Apply a replace inplace in the given file. Your regex pattern must 242 | ## take care of everything and might be more complex. Check the README 243 | ## for a complete copy-pastable example. 244 | ## 245 | OUTPUT_FILE = "CHANGELOG.rst" 246 | INSERT_POINT_REGEX = r'''(?isxu) 247 | ^ 248 | ( 249 | \s*Changelog\s*(\n|\r\n|\r) ## ``Changelog`` line 250 | ==+\s*(\n|\r\n|\r){2} ## ``=========`` rest underline 251 | ) 252 | 253 | ( ## Match all between changelog and release rev 254 | ( 255 | (?! 256 | (?<=(\n|\r)) ## look back for newline 257 | %(rev)s ## revision 258 | \s+ 259 | v?\([0-9]+-[0-9]{2}-[0-9]{2}\)(\n|\r\n|\r) ## date 260 | --+(\n|\r\n|\r) ## ``---`` underline 261 | ) 262 | . 263 | )* 264 | ) 265 | 266 | (?P%(rev)s) 267 | ''' % {'rev': r"v?[0-9]+\.[0-9]+(\.[0-9]+)?"} 268 | 269 | publish = FileRegexSubst(OUTPUT_FILE, INSERT_POINT_REGEX, r"\1\o\g") 270 | #publish = stdout 271 | 272 | ## ``revs`` is a list of callable or a list of string 273 | ## 274 | ## callable will be called to resolve as strings and allow dynamical 275 | ## computation of these. The result will be used as revisions for 276 | ## gitchangelog (as if directly stated on the command line). This allows 277 | ## to filter exaclty which commits will be read by gitchangelog. 278 | ## 279 | ## To get a full documentation on the format of these strings, please 280 | ## refer to the ``git rev-list`` arguments. There are many examples. 281 | ## 282 | ## Using callables is especially useful, for instance, if you 283 | ## are using gitchangelog to generate incrementally your changelog. 284 | ## 285 | ## Some helpers are provided, you can use them:: 286 | ## 287 | ## - FileFirstRegexMatch(file, pattern): will return a callable that will 288 | ## return the first string match for the given pattern in the given file. 289 | ## If you use named sub-patterns in your regex pattern, it'll output only 290 | ## the string matching the regex pattern named "rev". 291 | ## 292 | ## - Caret(rev): will return the rev prefixed by a "^", which is a 293 | ## way to remove the given revision and all its ancestor. 294 | ## 295 | ## Please note that if you provide a rev-list on the command line, it'll 296 | ## replace this value (which will then be ignored). 297 | ## 298 | ## If empty, then ``gitchangelog`` will act as it had to generate a full 299 | ## changelog. 300 | ## 301 | ## The default is to use all commits to make the changelog. 302 | 303 | revs = [ 304 | Caret(FileFirstRegexMatch(OUTPUT_FILE, INSERT_POINT_REGEX)), 305 | "HEAD" 306 | ] 307 | #revs = [] 308 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | build/ 3 | dist/ 4 | django_check_seo.egg-info/ 5 | .venv/ 6 | Pipfile 7 | Pipfile.lock 8 | .vscode/ 9 | *.pyc 10 | venv2 11 | venv3 12 | .coverage 13 | .python-version 14 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | multi_line_output=3 3 | include_trailing_comma=True 4 | force_grid_wrap=0 5 | use_parentheses=True 6 | line_length=88 7 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/ambv/black 3 | rev: 23.11.0 4 | hooks: 5 | - id: black 6 | files: ^.*\.py$ 7 | 8 | - repo: https://github.com/pre-commit/pre-commit-hooks 9 | rev: v4.5.0 10 | hooks: 11 | - id: check-added-large-files 12 | - id: check-merge-conflict 13 | - id: debug-statements 14 | 15 | - repo: https://github.com/PyCQA/isort 16 | rev: 5.12.0 17 | hooks: 18 | - id: isort 19 | files: ^.*\.py$ 20 | args: ["--profile", "black"] 21 | 22 | - repo: https://github.com/PyCQA/flake8 23 | rev: 6.1.0 24 | hooks: 25 | - id: flake8 26 | files: ^.*\.py$ 27 | 28 | - repo: https://github.com/rtts/djhtml 29 | rev: 3.0.6 30 | hooks: 31 | - id: djhtml 32 | args: [--tabwidth=2] 33 | files: ^.*\.html$ 34 | - id: djjs 35 | - id: djcss 36 | 37 | - repo: https://gitlab.com/kapt/open-source/git-hooks 38 | rev: v1.2.0 39 | hooks: 40 | - id: commit-msg 41 | always_run: true 42 | 43 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | * Corentin Bettiol 4 | * Adrien Delhorme 5 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 5 | v1.0.1 (2024-05-02) 6 | ------------------- 7 | 8 | Bug fixes 9 | ~~~~~~~~~ 10 | - Handle plural form in content [Corentin Bettiol] 11 | 12 | When searching for a keyword in the document (meta description, h1, h2, early content), handle plural of keyword too. 13 | 14 | For example, if the keyword is "destination", the checks will accept "destinations". 15 | Work at least for fr/en/es/pt 16 | 17 | 18 | Documentation 19 | ~~~~~~~~~~~~~ 20 | - Update README [Corentin Bettiol] 21 | - Update changelog [Corentin Bettiol] 22 | 23 | 24 | v1.0.0 (2024-01-25) 25 | ------------------- 26 | 27 | Features 28 | ~~~~~~~~ 29 | - Add permission "use_django_check_seo" [Corentin Bettiol] 30 | 31 | Documentation 32 | ~~~~~~~~~~~~~ 33 | - Add permission explanation in README [Corentin Bettiol] 34 | - Update changelog [Corentin Bettiol] 35 | 36 | 37 | v0.6.6 (2024-01-25) 38 | ------------------- 39 | 40 | Features 41 | ~~~~~~~~ 42 | - Add block in default template [Corentin Bettiol] 43 | 44 | close #5 45 | 46 | 47 | Documentation 48 | ~~~~~~~~~~~~~ 49 | - Improve README [Corentin Bettiol] 50 | 51 | Add instructions to replace template "seo_aside" block in README 52 | 53 | - Update changelog [Corentin Bettiol] 54 | 55 | Maintenance 56 | ~~~~~~~~~~~ 57 | - Move templates in their own folder [Corentin Bettiol] 58 | 59 | No more `templates/default.html` for django_check_seo 60 | 61 | 62 | 63 | v0.6.5 (2023-12-18) 64 | ------------------- 65 | 66 | Bug fixes 67 | ~~~~~~~~~ 68 | - The default_app_config application configuration variable is removed. 69 | [Dmytro Litvinov] 70 | 71 | Documentation 72 | ~~~~~~~~~~~~~ 73 | - Update changelog [Corentin Bettiol] 74 | 75 | Maintenance 76 | ~~~~~~~~~~~ 77 | - Update .pre-commit-config [Corentin Bettiol] 78 | 79 | 80 | v0.6.4 (2023-07-31) 81 | ------------------- 82 | 83 | Bug fixes 84 | ~~~~~~~~~ 85 | - Add missing dependency [Corentin Bettiol] 86 | 87 | Documentation 88 | ~~~~~~~~~~~~~ 89 | - Update README [Corentin Bettiol] 90 | - Update changelog [Corentin Bettiol] 91 | 92 | 93 | v0.6.3 (2023-07-28) 94 | ------------------- 95 | 96 | Bug fixes 97 | ~~~~~~~~~ 98 | - Fix #58 - remove unused load cms_tags [Corentin Bettiol] 99 | 100 | Documentation 101 | ~~~~~~~~~~~~~ 102 | - Update changelog [Corentin Bettiol] 103 | 104 | 105 | v0.6.2 (2023-04-19) 106 | ------------------- 107 | 108 | Documentation 109 | ~~~~~~~~~~~~~ 110 | - Update changelog [Corentin Bettiol] 111 | 112 | Other 113 | ~~~~~ 114 | - An issue when splitting the keywords [Ahmed Shawky] 115 | 116 | Keywords must be separated by ", " a comma followed by two empty spaces, which will fail in most cases. 117 | Split by, then removing empty spaces should solve the issue 118 | 119 | 120 | 121 | v0.6.1 (2023-04-18) 122 | ------------------- 123 | 124 | Documentation 125 | ~~~~~~~~~~~~~ 126 | - Update readme [Corentin Bettiol] 127 | - Update changelog [Corentin Bettiol] 128 | 129 | Other 130 | ~~~~~ 131 | - Have the test-client follow redirects on requests [Marco Bonetti] 132 | 133 | 134 | v0.6.0 (2023-03-03) 135 | ------------------- 136 | 137 | Documentation 138 | ~~~~~~~~~~~~~ 139 | - Update readme [Corentin Bettiol] 140 | - Update changelog [Corentin Bettiol] 141 | 142 | Other 143 | ~~~~~ 144 | - #55 Support both new and old versions of Django still using 145 | ugettext_lazy [Marco Bonetti] 146 | - Dont report missing description if we have exactly one [Marco Bonetti] 147 | - Django 4: ugettext_lazy was removed in favor of gettext_lazy [Marco 148 | Bonetti] 149 | 150 | 151 | v0.5.2 (2022-09-21) 152 | ------------------- 153 | 154 | Bug fixes 155 | ~~~~~~~~~ 156 | - Fix python2 tests [Corentin Bettiol] 157 | 158 | Maintenance 159 | ~~~~~~~~~~~ 160 | - Python2 (!!!) compat [Corentin Bettiol] 161 | - Improve (?) django 3 compat [Corentin Bettiol] 162 | 163 | replace a check "startswith 2" by "> 1" in order to handle django 164 | version 3 and mor 165 | 166 | 167 | 168 | v0.5.1 (2022-06-17) 169 | ------------------- 170 | 171 | Bug fixes 172 | ~~~~~~~~~ 173 | - Fix #45 Meta description check is now working [Corentin Bettiol] 174 | 175 | 176 | v0.5.0 (2022-06-14) 177 | ------------------- 178 | 179 | Features 180 | ~~~~~~~~ 181 | - Fix #48 [Corentin Bettiol] 182 | 183 | Fix typo in comments. 184 | 185 | Update image check: image lacking alt tags are creating a "warning" and 186 | not an "error" anymore, since there are valid usecases where you won't 187 | add an alt tag (non-text content). 188 | 189 | Update of translations 190 | 191 | 192 | Documentation 193 | ~~~~~~~~~~~~~ 194 | - Update readme [Corentin Bettiol] 195 | 196 | update pre-commit-confi 197 | 198 | - Update changelog [Adrien Delhorme] 199 | 200 | Other 201 | ~~~~~ 202 | - Remove requests as a requirement, use django.test.Client [Jeffrey de 203 | Lange] 204 | 205 | 206 | v0.4.3 (2021-09-09) 207 | ------------------- 208 | 209 | Documentation 210 | ~~~~~~~~~~~~~ 211 | - Update changelog [Adrien Delhorme] 212 | 213 | Maintenance 214 | ~~~~~~~~~~~ 215 | - Correct MANIFEST.in [Adrien Delhorme] 216 | 217 | 218 | v0.4.2 (2021-09-09) 219 | ------------------- 220 | 221 | Bug fixes 222 | ~~~~~~~~~ 223 | - Display file name instead of string "image" [Adrien Delhorme] 224 | - Misleading translation for image alt check [Adrien Delhorme] 225 | - Correct handling of unicode strings [Adrien Delhorme] 226 | - Check_keyword_url with accents [Adrien Delhorme] 227 | 228 | When the keywords contained accents and spaces and the url contained 229 | accents, the check was failing 230 | 231 | 232 | Documentation 233 | ~~~~~~~~~~~~~ 234 | - Update AUTHORS file [Adrien Delhorme] 235 | - Update logo in readme for dark theme [Corentin Bettiol] 236 | - Update changelog [cb] 237 | 238 | Maintenance 239 | ~~~~~~~~~~~ 240 | - Ignore .python-version file [Adrien Delhorme] 241 | 242 | 243 | v0.4.1 (2021-08-23) 244 | ------------------- 245 | 246 | Bug fixes 247 | ~~~~~~~~~ 248 | - Replace spaces by dash in keyword for url search [cb] 249 | 250 | Documentation 251 | ~~~~~~~~~~~~~ 252 | - Update readme [cb] 253 | - Update changelog [cb] 254 | 255 | 256 | v0.4.0 (2021-03-25) 257 | ------------------- 258 | 259 | Features 260 | ~~~~~~~~ 261 | - Add setting to allow authenticated requests to follow redirections 262 | [cb] 263 | 264 | * close #43 265 | * update black in .pre-commit-config & run blac 266 | 267 | 268 | Bug fixes 269 | ~~~~~~~~~ 270 | - Use no-store instead of no-cache [cb] 271 | 272 | (more info here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#cacheability 273 | 274 | 275 | 276 | v0.3.7 (2021-01-06) 277 | ------------------- 278 | 279 | Bug fixes 280 | ~~~~~~~~~ 281 | - Fix #42 [cb] 282 | 283 | *djangocms toolbar button now does not return a str containing a string representation of a byte string anymor 284 | 285 | - Update broken setup.cfg [cb] 286 | - Remove print [cb] 287 | 288 | 289 | v0.3.6 (2020-09-14) 290 | ------------------- 291 | 292 | Bug fixes 293 | ~~~~~~~~~ 294 | - No summary [cb] 295 | 296 | *fix python2 issue 'No module named unidecode 297 | 298 | 299 | 300 | v0.3.5 (2020-09-14) 301 | ------------------- 302 | 303 | Features 304 | ~~~~~~~~ 305 | - Fix launc_tests exit codes [cb] 306 | - Update keyword matchin in URL [cb] 307 | 308 | *add tests for keywords in URL 309 | *update tests for h 310 | 311 | 312 | Documentation 313 | ~~~~~~~~~~~~~ 314 | - Add CONTRIBUTING.md [cb] 315 | 316 | *Add contributing guidelines 317 | *Add AUTHORS.md file (list of contributors) 318 | *Edit README.md 319 | *Edit launch_tests.sh 320 | *Edit pre-commit confi 321 | 322 | 323 | Maintenance 324 | ~~~~~~~~~~~ 325 | - Run flake8, isort & black [cb] 326 | 327 | Tests 328 | ~~~~~ 329 | - Add tests for keywords in 1st paragraph [cb] 330 | - Test url & content length [cb] 331 | - Add tests *for internal & external links [cb] 332 | - Add tests for check_keywords [cb] 333 | - Fix failing tests in python2 [cb] 334 | 335 | Other 336 | ~~~~~ 337 | - #40 [cb] 338 | 339 | *current url is now fetched using utf-8, which will not throw exception if accentuated char is foun 340 | 341 | - Update pre-commit & launch_checks [cb] 342 | - Update pre-commit [cb] 343 | - +TEST [cb] 344 | 345 | *switch title kw check to the new method (using regex) 346 | *add tests for titl 347 | 348 | - Add image tests remove unnecessary tags from test html [cb] 349 | - Bug + add tests for h2 [cb] 350 | 351 | *fix bug where keyword was not found but green bold was added to part of the wor 352 | 353 | - Add tests for meta description [cb] 354 | - Add tests for check_description [cb] 355 | - Update tests [cb] 356 | 357 | *remove some foldrs & files from coverage repor 358 | 359 | - Update coding in test_h1.py (from latin-1 to utf-8) [cb] 360 | - Bug in check_h1 [cb] 361 | 362 | *finished adding tests for check_h1.py, enhoy 100% coverage! 363 | *fix bug from previous commit (forgot parenthesis 364 | 365 | - Add some h1 tests & fix bug [cb] 366 | 367 | fix h1 bug: no text was displayed in searched_in where all content was in the alt tag of an image (thx tests! 368 | 369 | 370 | 371 | v0.3.4 (2020-03-24) 372 | ------------------- 373 | - Bad strings in some regex [cb] 374 | 375 | 376 | v0.3.3 (2020-03-24) 377 | ------------------- 378 | 379 | Features 380 | ~~~~~~~~ 381 | - Add test skeleton (will soon add real unit tests) [cb] 382 | 383 | Other 384 | ~~~~~ 385 | - #37, fix #38 [cb] 386 | 387 | * replace number by keywords that are found inside text 388 | * update regex used to count keyword occurences to accept some special chars (including @ 389 | 390 | 391 | 392 | v0.3.2 (2020-03-04) 393 | ------------------- 394 | 395 | Maintenance 396 | ~~~~~~~~~~~ 397 | - Update README & check_title [cb] 398 | 399 | * fix typo README 400 | * fix check_title: case "empty title tag" was not handle 401 | 402 | 403 | Other 404 | ~~~~~ 405 | - +MAINT: check_title [cb] 406 | 407 | * replace .string by .text 408 | * handle case where title tag exist but does not contain any text (display [no content]) 409 | * add french translation for "[no content] 410 | 411 | 412 | 413 | v0.3.1 (2020-03-03) 414 | ------------------- 415 | 416 | Bug fixes 417 | ~~~~~~~~~ 418 | - Description was lowered but no keywords [cb] 419 | 420 | * "check my super duper description" does not contain "Super" 421 | * "check my super duper description" contain "super 422 | 423 | - Empty links error [cb] 424 | 425 | * links with only newlines were considered as valid strings, now they should display their content tag instead (usually an img without alt tag 426 | 427 | - Meta description error [cb] 428 | 429 | * .join() in python2 is not encoding-safe, so strings like "Thaïs" in meta description or h1 could lead to an erro 430 | 431 | - Meta description searched_in [cb] 432 | 433 | * display lower() meta description to match with lower() keywords in searched_in var 434 | 435 | 436 | 437 | v0.3.0 (2020-03-02) 438 | ------------------- 439 | - +FIX+DOCS [cb] 440 | 441 | *add new DJANGO_CHECK_SEO_SEARCH_IN parameter (fix #30, #32 & #35) 442 | *fix error in searched_in for meta descriptions tests (fix #36 443 | 444 | - Display arrows & update cursor for list of checks [cb] 445 | 446 | 447 | v0.2.0 (2020-02-28) 448 | ------------------- 449 | 450 | Documentation 451 | ~~~~~~~~~~~~~ 452 | - Mention custom djangocms-page-meta version for install on django < 453 | 1.11 [cb] 454 | 455 | Other 456 | ~~~~~ 457 | - & FEAT: [cb] 458 | 459 | * slugify urls & keywords (fix #33) 460 | * show what is wrong (or good) in the "searched in" sections (fix #34) 461 | * no more empty links in "searched in" sections : 462 | - you should see content of alt tag if it exists in an image in your link 463 | - if there is no image in your link, you should see the html code of the first chil 464 | 465 | - Update default settings: [cb] 466 | 467 | set link depth to 4 instead of 468 | 469 | - Correct typo, add colors in "searched in" sections [cb] 470 | 471 | 472 | v0.1.1 (2020-02-05) 473 | ------------------- 474 | 475 | Bug fixes 476 | ~~~~~~~~~ 477 | - Fix html tags order in template [cb] 478 | 479 | 480 | v0.1.0 (2020-02-05) 481 | ------------------- 482 | 483 | Features 484 | ~~~~~~~~ 485 | - Mention that the check is done on public page only [cb] 486 | 487 | * update translations 488 | * add cs 489 | 490 | 491 | 492 | v0.0.12 (2020-02-05) 493 | -------------------- 494 | 495 | Bug fixes 496 | ~~~~~~~~~ 497 | - Ignore title tags in body [cb] 498 | 499 | close #28: check for a title meta tag only inside in the application page which will display formatted content without htm 766 | 767 | - Add new check [cb] 768 | 769 | check 17: url is shorter than 'max_url_length' char 770 | 771 | - Bug [cb] 772 | 773 | check for alt attribute in img tags was not functionnal and returned a keyerro 774 | 775 | - Remove prints, correct division by zero error [cb] 776 | - Wagnings to warnings, change the way keywords occurences checks work 777 | [cb] 778 | 779 | use percentage of words instead of an interva 780 | 781 | - #2 [cb] 782 | 783 | number of links now trigger a warning instead of a proble 784 | 785 | - Add new check, remove stop-words [cb] 786 | 787 | check 17: count words in main conten 788 | 789 | - Remove mention of nltk [cb] 790 | - Add list of features on readme, update text, add extracted content in 791 | context vars [cb] 792 | - Add new check [cb] 793 | 794 | check 16: ensure that at least a keyword is in the first X words of conten 795 | 796 | - Add new checks [cb] 797 | 798 | check 14: ensure alt presence in images 799 | check 15: check path level 800 | 801 | - Update screenshot in readme [cb] 802 | - Add new checks [cb] 803 | 804 | check 10: keywords present in h2 tags (and h2 tags are present in page) 805 | check 11: meta description is present 806 | check 12: meta description length 807 | check 13: keywords present in meta descriptio 808 | 809 | - Broken english [cb] 810 | - Change template view, add new checks [cb] 811 | 812 | check 7: keyword is present in url 813 | check 8: h1 is present exactly 1 time 814 | check 9: keyword is present in h 815 | 816 | - Add new checks [cb] 817 | 818 | check 5: number of internal & external links 819 | check 6: occurrence of keyword 820 | 821 | - Use beautiful soup to parse html content, add firsts checks [cb] 822 | 823 | check 1: check if title is present on the page\ncheck 2: check title length\ncheck 3: get keywords\ncheck 4: check if at least a keyword is in page titl 824 | 825 | - Add readme [cb] 826 | - First run of black, isort & flake8, update gitignore, add comment [cb] 827 | - Update name to reflect actual git repo name [cb] 828 | - Initial commit [Corentin Bettiol] 829 | - First commit, testing things [cb] 830 | 831 | 832 | 833 | 834 | .. Generated by gitchangelog 835 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | How can you contribute to this project ? 4 | 5 | ## Add issue 6 | 7 | You can help by finding new issues & reporting them by creating [new issues](https://github.com/kapt-labs/django-check-seo/issues). 8 | 9 | ## Add code 10 | 11 | You can help by picking an issue, or choosing to add a new test/feature (create an issue before you start coding). 12 | 13 | 0. Create a new issue, receive positive feedback. 14 | 15 | 1. Fork the repo, clone it. 16 | 17 | 2. Install pre-commit & unit tests dependencies. 18 | ```bash 19 | python3 -m pip install pre-commit python3-venv 20 | python2 -m pip install virtualenv 21 | ``` 22 | 23 | 3. Install pre-commit hooks. 24 | ```bash 25 | pre-commit install 26 | ``` 27 | 28 | 4. Create new branch. 29 | ```bash 30 | git checkout -b mybranch 31 | ``` 32 | 33 | 5. Add your code. 34 | 35 | 6. (*Facultative*) Add tests ? 36 | 37 | 7. Add yourself in [AUTHORS.md](AUTHORS.md). 38 | 39 | 8. Commit, push. 40 | *Make sure that pre-commit runs isort, black, flake8 & `launch_checks.sh`. [Example](https://github.com/kapt-labs/django-check-seo/commit/da1d0be5d3ebe6734585cd5dd7027186d432ccd0#commitcomment-38147459).* 41 | 42 | 9. Create a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request). 43 | 44 | 10. ![That's all folks!](https://i.imgur.com/o2Tcd2E.png) 45 | 46 | ---- 47 | 48 | ### Commit description guidelines 49 | 50 | We're using bluejava's [git-commit-guide](https://github.com/bluejava/git-commit-guide) for our commits description. Here's a quick reference: 51 | 52 | ![Reference git-commit-guide](https://raw.githubusercontent.com/bluejava/git-commit-guide/master/gitCommitMsgGuideQuickReference.png) 53 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include CONTRIBUTING.md 4 | include AUTHORS.md 5 | recursive-include django_check_seo/static/ * 6 | recursive-include django_check_seo/templates/ *.html 7 | recursive-include django_check_seo/locale/ * 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Django Check SEO](https://user-images.githubusercontent.com/45763865/114545606-72178380-9c5c-11eb-99dd-1088bb2a0bd9.png) 2 | 3 | *Replacing some features of Yoast or SEMrush for Django & Django-CMS users.* 4 | 5 | In other words, django-check-seo will tell you if you have problems concerning a broad range of SEO aspects of your pages. 6 | 7 | ---- 8 | 9 | [![PyPI](https://img.shields.io/pypi/v/django-check-seo?color=%232a2)](https://pypi.org/project/django-check-seo/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/django-check-seo?color=%232a2)](https://pypi.org/project/django-check-seo/) [![GitHub last commit](https://img.shields.io/github/last-commit/kapt-labs/django-check-seo)](https://github.com/kapt-labs/django-check-seo) 10 | 11 | ---- 12 | 13 | # Install 14 | 15 | *Only for django >= 2.2 & python >= 3.7, see [here](https://github.com/kapt-labs/django-check-seo/tree/python2) for a python2/django 1.8-1.11 version (tl;dr: install version <0.6).* 16 | 17 | 1. Install the module from [PyPI](https://pypi.org/project/django-check-seo/): 18 | ``` 19 | python3 -m pip install django-check-seo 20 | ``` 21 | 22 | 2. Add it in your `INSTALLED_APPS`: 23 | ``` 24 | "django_check_seo", 25 | ``` 26 | 27 | 3. Add this in your `urls.py` *(if you're using django-cms, put it before the `cms.urls` line or it will not work)*: 28 | ``` 29 | path("django-check-seo/", include("django_check_seo.urls")), 30 | ``` 31 | 4. Update your Django [Site](https://i.imgur.com/pNRsKs7.png) object parameters with a working url (here's an [example](https://i.imgur.com/IedF3xE.png) for dev environment). 32 | 33 | 5. Add `testserver` (and maybe `www.testserver`) to your `ALLOWED_HOSTS` list in your settings.py (django-check-seo uses the Test Framework in order to get content, instead of doing an HTTP request). 34 | 35 | 6. ![**new in 1.0.0**](https://img.shields.io/badge/new_in-1.0.0-green) Add the permission (`use_django_check_seo`) to the users/groups you want to give access to. 36 | 37 | 7. *(optional) Configure the settings (see [config](#config)).* 38 | 39 | 8. ![that's all folks!](https://i.imgur.com/o2Tcd2E.png) 40 | 41 | ---- 42 | 43 | # Misc 44 | 45 | This application needs `beautifulsoup4` (>=4.7.0) and `djangocms_page_meta` *(==0.8.5 if using django < 1.11)*. It may be used with or without `django-cms` (a django-check-seo button will appear in the topbar if you're using django-cms). 46 | 47 | If you're not using Django CMS (only Django), here's the link format to access your pages reports: 48 | 49 | ``` 50 | https://example.com/django-check-seo/?page=/example-page/ 51 | -> will check https://example.com/example-page/ 52 | 53 | https://example.com/fr/django-check-seo/?page=/example-page/ 54 | -> will check https://example.com/example-page/ 55 | (using localized url (if you add django-check-seo in i18n_patterns)) 56 | ``` 57 | 58 | ---- 59 | 60 | # Config 61 | 62 | ## Basic settings 63 | 64 | The basic config (used by default) is located in [`django-check-seo/conf/settings.py`](https://github.com/kapt-labs/django-check-seo/blob/master/django_check_seo/conf/settings.py#L5-L15) and looks like this: 65 | ```python 66 | DJANGO_CHECK_SEO_SETTINGS = { 67 | "content_words_number": [300, 600], 68 | "internal_links": 1, 69 | "external_links": 1, 70 | "meta_title_length": [30, 60], 71 | "meta_description_length": [50, 160], 72 | "keywords_in_first_words": 50, 73 | "max_link_depth": 3, 74 | "max_url_length": 70, 75 | } 76 | ``` 77 | 78 | If you need to change something, just define a dict named `DJANGO_CHECK_SEO_SETTINGS` in your settings.py. 79 | 80 | ### *Custom settings example:* 81 | 82 | If you put this in your `settings.py` file: 83 | 84 | ```python 85 | DJANGO_CHECK_SEO_SETTINGS = { 86 | "internal_links": 25, 87 | "meta_title_length": [15,30], 88 | } 89 | ``` 90 | 91 | Then this will be the settings used by the application: 92 | 93 | ```python 94 | DJANGO_CHECK_SEO_SETTINGS = { 95 | "content_words_number": [300, 600], 96 | "internal_links": 25, # 1 if using default settings 97 | "external_links": 1, 98 | "meta_title_length": [15,30], # [30, 60] if using default settings 99 | "meta_description_length": [50, 160], 100 | "keywords_in_first_words": 50, 101 | "max_link_depth": 3, 102 | "max_url_length": 70, 103 | } 104 | ``` 105 | 106 | *Want to know more ? See the wiki page [Settings explained](https://github.com/kapt-labs/django-check-seo/wiki/Settings-explained).* 107 | 108 | ## Templates 109 | 110 | The `django_check_seo/default.html` template have an `