88"""
99import logging
1010from datetime import datetime
11+ from fnmatch import fnmatch
1112from pathlib import Path
1213from subprocess import CalledProcessError
13- from typing import List , Optional
14+ from typing import List
1415
1516from mkdocs .config import config_options as c
1617from mkdocs .plugins import BasePlugin
2021from neoteroi .mkdocs .contribs .domain import ContributionsReader , Contributor
2122from neoteroi .mkdocs .contribs .git import GitContributionsReader
2223from neoteroi .mkdocs .contribs .html import ContribsViewOptions , render_contribution_stats
24+ from neoteroi .mkdocs .contribs .txt import TXTContributionsReader
2325
2426logger = logging .getLogger ("MARKDOWN" )
2527
2628
29+ class DefaultContributionsReader (ContributionsReader ):
30+ """
31+ Supports both contributors obtained from Git history and from configuration files.
32+ """
33+
34+ def __init__ (self ) -> None :
35+ super ().__init__ ()
36+ self ._git_reader = GitContributionsReader ()
37+ self ._txt_reader = TXTContributionsReader ()
38+
39+ def get_contributors (self , file_path : Path ) -> List [Contributor ]:
40+ git_history_contributors = self ._git_reader .get_contributors (file_path )
41+ configured_contributors = self ._txt_reader .get_contributors (file_path )
42+ return list (
43+ {
44+ item .email : item
45+ for item in configured_contributors + git_history_contributors
46+ }.values ()
47+ )
48+
49+ def get_last_modified_date (self , file_path : Path ) -> datetime :
50+ return self ._git_reader .get_last_modified_date (file_path )
51+
52+
2753class ContribsPlugin (BasePlugin ):
2854 _contribs_reader : ContributionsReader
2955 config_scheme = (
@@ -33,16 +59,14 @@ class ContribsPlugin(BasePlugin):
3359 ("contributors" , c .Type (list , default = [])),
3460 ("show_last_modified_time" , c .Type (bool , default = True )),
3561 ("show_contributors_title" , c .Type (bool , default = False )),
62+ ("exclude" , c .Type (list , default = [])),
3663 )
3764
3865 def __init__ (self ) -> None :
3966 super ().__init__ ()
40- self ._contribs_reader = GitContributionsReader ()
41-
42- def _read_contributor_merge_with (self , contributor_info ) -> Optional [str ]:
43- return contributor_info .get ("merge_with" )
67+ self ._contribs_reader = DefaultContributionsReader ()
4468
45- def _handle_merge_contributor_info (
69+ def _merge_contributor_by_email (
4670 self ,
4771 contributors : List [Contributor ],
4872 contributor : Contributor ,
@@ -67,6 +91,7 @@ def _handle_merge_contributor_info(
6791 if parent :
6892 parent .count += contributor .count
6993 return True
94+
7095 return False
7196
7297 def _get_contributors (self , page_file : File ) -> List [Contributor ]:
@@ -83,6 +108,7 @@ def _get_contributors(self, page_file: File) -> List[Contributor]:
83108 contributor_info = next (
84109 (item for item in info if item .get ("email" ) == contributor .email ), None
85110 )
111+
86112 if contributor_info :
87113 contributor .image = contributor_info .get ("image" )
88114 contributor .key = contributor_info .get ("key" )
@@ -91,8 +117,24 @@ def _get_contributors(self, page_file: File) -> List[Contributor]:
91117 # ignore the contributor's information (can be useful for bots)
92118 continue
93119
120+ if (
121+ "name" in contributor_info
122+ and contributor_info ["name" ] != contributor .name
123+ ):
124+ parent = next (
125+ (
126+ other
127+ for other in contributors
128+ if other .name == contributor_info ["name" ]
129+ ),
130+ None ,
131+ )
132+ if parent :
133+ parent .count += contributor .count
134+ continue
135+
94136 # should contributor information be merged with another object?
95- if self ._handle_merge_contributor_info (
137+ if self ._merge_contributor_by_email (
96138 contributors , contributor , contributor_info
97139 ):
98140 # skip this item as it was merged with another one
@@ -103,7 +145,7 @@ def _get_contributors(self, page_file: File) -> List[Contributor]:
103145 return results
104146
105147 def _get_last_commit_date (self , page_file : File ) -> datetime :
106- return self ._contribs_reader .get_last_commit_date (
148+ return self ._contribs_reader .get_last_modified_date (
107149 Path ("docs" ) / page_file .src_path
108150 )
109151
@@ -127,7 +169,18 @@ def _set_contributors(self, markdown: str, page: Page) -> str:
127169 )
128170 )
129171
172+ def _is_ignored_page (self , page : Page ) -> bool :
173+ if not self .config .get ("exclude" ):
174+ return False
175+
176+ return any (
177+ fnmatch (page .file .src_path , ignored_pattern )
178+ for ignored_pattern in self .config ["exclude" ]
179+ )
180+
130181 def on_page_markdown (self , markdown , * args , ** kwargs ):
182+ if self ._is_ignored_page (kwargs ["page" ]):
183+ return markdown
131184 try :
132185 markdown = self ._set_contributors (markdown , kwargs ["page" ])
133186 except (CalledProcessError , ValueError ) as operation_error :
0 commit comments