|
60 | 60 |
|
61 | 61 | The link checker notices within a few seconds when the exclusion file has been modified. |
62 | 62 | Examine the link checker's log file to confirm that it has been reloaded successfully. |
| 63 | +
|
| 64 | +There is also the option to exclude identifiers based on what they start with which will |
| 65 | +usually be used to exclude shoulders. |
| 66 | +
|
| 67 | + LINKCHECKER_ID_EXCLUSION_ENABLED = True |
| 68 | + LINKCHECKER_ID_EXCLUSION_FILE = 'path/to/id_exclusion_file.txt' |
63 | 69 | """ |
64 | 70 |
|
65 | 71 | # noinspection PyUnresolvedReferences |
@@ -119,12 +125,22 @@ def __init__(self): |
119 | 125 | self._temporaryExcludes = [] |
120 | 126 | self._exclusionFile = None |
121 | 127 |
|
| 128 | + self._idExclusionFileModifyTime = -1 |
| 129 | + self._idLastExclusionFileCheckTime = -1 |
| 130 | + self._idExclusionFile = None |
| 131 | + self._idExclusionTuple = None |
| 132 | + |
122 | 133 | def run(self): |
123 | 134 | if django.conf.settings.LINKCHECKER_EXCLUSION_ENABLED: |
124 | 135 | if django.conf.settings.LINKCHECKER_EXCLUSION_FILE is not None: |
125 | 136 | self._exclusionFile = django.conf.settings.LINKCHECKER_EXCLUSION_FILE |
126 | 137 | log.info(f"Link checker exclusion enabled with file: {self._exclusionFile}") |
127 | 138 |
|
| 139 | + if django.conf.settings.LINKCHECKER_ID_EXCLUSION_ENABLED: |
| 140 | + if django.conf.settings.LINKCHECKER_ID_EXCLUSION_FILE is not None: |
| 141 | + self._idExclusionFile = django.conf.settings.LINKCHECKER_ID_EXCLUSION_FILE |
| 142 | + log.info(f"Link checker ID exclusion enabled with file: {self._idExclusionFile}") |
| 143 | + |
128 | 144 | while not self.terminated(): |
129 | 145 | self.check_all() |
130 | 146 |
|
@@ -227,6 +243,44 @@ def remaining(self, start, cycle): |
227 | 243 | def daysSince(self, when): |
228 | 244 | return int((self.now() - when) / 86400) |
229 | 245 |
|
| 246 | + def loadIdExclusionFile(self): |
| 247 | + if self._idExclusionFile is None: |
| 248 | + return |
| 249 | + if self.now_int() - self._idLastExclusionFileCheckTime < 10: |
| 250 | + return |
| 251 | + self._idLastExclusionFileCheckTime = self.now_int() |
| 252 | + f = None |
| 253 | + s = None |
| 254 | + try: |
| 255 | + # noinspection PyTypeChecker |
| 256 | + s = os.stat(self._idExclusionFile) |
| 257 | + if s.st_mtime == self._idExclusionFileModifyTime: |
| 258 | + return |
| 259 | + # noinspection PyTypeChecker |
| 260 | + f = open(self._idExclusionFile) |
| 261 | + |
| 262 | + id_exclusion = [] |
| 263 | + n = 0 |
| 264 | + for l in f: |
| 265 | + n += 1 |
| 266 | + if l.strip() == "" or l.startswith("#"): |
| 267 | + continue |
| 268 | + |
| 269 | + id_exclusion.append(l.strip().casefold()) # Store as lowercase for case-insensitive matching |
| 270 | + |
| 271 | + self._idExclusionTuple = tuple(id_exclusion) |
| 272 | + self._exclusionFileModifyTime = s.st_mtime |
| 273 | + log.info("id exclusion file successfully loaded") |
| 274 | + except Exception as e: |
| 275 | + log.error('Exception') |
| 276 | + if s is not None: |
| 277 | + self._exclusionFileModifyTime = s.st_mtime |
| 278 | + log.error("error loading exclusion file: " + str(e)) |
| 279 | + finally: |
| 280 | + if f is not None: |
| 281 | + f.close() |
| 282 | + |
| 283 | + |
230 | 284 | def loadExclusionFile(self): |
231 | 285 | if self._exclusionFile is None: |
232 | 286 | return |
@@ -288,14 +342,20 @@ def harvest(self, model, only=None, filter=None): |
288 | 342 | if len(qs) == 0: |
289 | 343 | break |
290 | 344 | for o in qs: |
| 345 | + # added to exclude ID patterns if they match the id exclusion tuple in addition to normal filtering |
291 | 346 | if filter is None or filter(o): |
292 | | - # log.debug(f'Generator returning: {str(o)}') |
293 | | - yield o |
| 347 | + if self._idExclusionTuple is None or (o.identifier is not None and |
| 348 | + not o.identifier.casefold().startswith(self._idExclusionTuple)): |
| 349 | + yield o |
| 350 | + else: |
| 351 | + log.debug('Skipping identifier %s due to ID shoulder exclusion', o.identifier) |
| 352 | + |
294 | 353 | lastIdentifier = qs[-1].identifier |
295 | 354 | yield None |
296 | 355 |
|
297 | 356 | def updateDatabaseTable(self): |
298 | 357 | self.loadExclusionFile() |
| 358 | + self.loadIdExclusionFile() |
299 | 359 | log.info("begin update table") |
300 | 360 | numIdentifiers = 0 |
301 | 361 | numAdditions = 0 |
@@ -413,6 +473,7 @@ def updateDatabaseTable(self): |
413 | 473 | def loadWorkset(self): |
414 | 474 | self._workset = None |
415 | 475 | self.loadExclusionFile() |
| 476 | + self.loadIdExclusionFile() |
416 | 477 | log.info("begin load workset") |
417 | 478 | _workset = [] |
418 | 479 | numOwnersCapped = 0 |
@@ -490,6 +551,7 @@ def getNextLink(self): |
490 | 551 | _lock.acquire() |
491 | 552 | try: |
492 | 553 | self.loadExclusionFile() |
| 554 | + self.loadIdExclusionFile() |
493 | 555 | startingIndex = self._index |
494 | 556 | allFinished = True |
495 | 557 | t = self.now() |
|
0 commit comments