webcode-tk 1.0.0__tar.gz → 1.0.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: webcode-tk
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary:
5
5
  Home-page: https://github.com/HundredVisionsGuy/webcode-tk
6
6
  License: MIT
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
19
20
  Requires-Dist: MechanicalSoup (>=1.1.0,<2.0.0)
20
21
  Requires-Dist: bs4 (>=0.0.1,<0.0.2)
21
22
  Requires-Dist: file-clerk (>=1.0.9,<2.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "webcode-tk"
3
- version = "1.0.0"
3
+ version = "1.0.2"
4
4
  description = ""
5
5
  authors = ["hundredvisionsguy <cwinikka@gmail.com>"]
6
6
  keywords = ["html", "validation", "css", ]
@@ -18,4 +18,4 @@ Modules exported by this package:
18
18
  - `validator_tools`: sends HTML or CSS code to the W3C Validator to check
19
19
  for errors.
20
20
  """
21
- __version__ = "1.0.0"
21
+ __version__ = "1.0.2"
@@ -1493,7 +1493,7 @@ def update_contrast_results(
1493
1493
 
1494
1494
 
1495
1495
  def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
1496
- """returns a list of all all failures or a pass based on rating
1496
+ """returns a list of all failures or a pass based on rating
1497
1497
 
1498
1498
  We'll create a recursive inner nested function to traverse the DOM. We will
1499
1499
  target only the styles that are applied directly so as to not overwhelm the
@@ -1567,7 +1567,12 @@ def get_color_contrast_details(tree: CSSAppliedTree, rating="AAA") -> list:
1567
1567
 
1568
1568
  for element in elements:
1569
1569
  check_element(element, rating, filename)
1570
- if not results:
1570
+ if results:
1571
+ adjusted = []
1572
+ for result in results:
1573
+ adjusted.append("fail: " + result)
1574
+ results = adjusted
1575
+ else:
1571
1576
  # It's a success! Let's let them know
1572
1577
  msg = f"success: {filename} passes color contrast for {rating} Normal"
1573
1578
  msg += " and Large."
@@ -2222,6 +2222,245 @@ def is_link_selector(selector: str) -> bool:
2222
2222
  return bool(selector_match)
2223
2223
 
2224
2224
 
2225
+ def get_all_project_stylesheets(project_dir: str) -> list:
2226
+ """returns a list of all styles and stylesheets from a project folder.
2227
+
2228
+ This includes styles from style tags as well as linked stylesheets.
2229
+
2230
+ Args:
2231
+ project_dir: the relative link to the folder with the web docs.
2232
+
2233
+ Returns:
2234
+ all_files_styles: a list of all stylesheets and style tag contents.
2235
+ """
2236
+ directory = project_dir
2237
+ html_files = clerk.get_all_files_of_type(directory, "html")
2238
+ all_files_styles = []
2239
+ for file in html_files:
2240
+ filename = clerk.get_file_name(file)
2241
+ stylesheets = get_all_stylesheets_by_file(file)
2242
+ all_files_styles.append((filename, stylesheets))
2243
+ return all_files_styles
2244
+
2245
+
2246
+ def no_style_attributes_allowed_report(project_dir: str) -> list:
2247
+ """returns a report on whether HTML docs use style attributes or not.
2248
+
2249
+ Only call this report if you do not allow a style attribute in an
2250
+ HTML doc
2251
+
2252
+ Args:
2253
+ project_dir: the relative link to the folder with the web docs.
2254
+
2255
+ Returns:
2256
+ report: a list of all HTML docs and a pass or fail message.
2257
+ """
2258
+ report = []
2259
+
2260
+ html_files = clerk.get_all_files_of_type(project_dir, "html")
2261
+ for file in html_files:
2262
+ try:
2263
+ has_style_attr = html_tools.has_style_attribute_data(file)
2264
+ except AttributeError:
2265
+ continue
2266
+ if has_style_attr:
2267
+ result = f"fail: {file} uses style attributes"
2268
+ else:
2269
+ result = f"pass: {file} does not use style attributes"
2270
+ report.append(result)
2271
+ return report
2272
+
2273
+
2274
+ def styles_applied_report(project_dir: str) -> list:
2275
+ """returns a report of all files in a project folder that apply styles
2276
+
2277
+ This lets us know for each HTML doc if they apply styles (pass) or
2278
+ if they do not apply styles (fail)
2279
+
2280
+ Args:
2281
+ project_dir: a relative path to the project folder.
2282
+
2283
+ Returns:
2284
+ report: a list of HTML docs and whether they pass or fail (pass) means
2285
+ they did apply styles and fail is the opposite.
2286
+ """
2287
+ report = []
2288
+ html_files = clerk.get_all_files_of_type(project_dir, "html")
2289
+ for file in html_files:
2290
+ styles = get_all_stylesheets_by_file(file)
2291
+ if not styles:
2292
+ results = f"fail: {file} does NOT apply CSS."
2293
+ else:
2294
+ results = f"pass: {file} applies CSS."
2295
+ report.append(results)
2296
+ return report
2297
+
2298
+
2299
+ def fonts_applied_report(project_dir: str, min=1, max=2) -> list:
2300
+ """returns a report of all files in a project folder that apply font
2301
+ families.
2302
+
2303
+ You can set the minimum and maximum number of fonts applied per page.
2304
+
2305
+ Args:
2306
+ project_dir: the relative path to the project folder we want to check.
2307
+ min: the minimum number of fonts applied per file.
2308
+ max: the maximum number of fonts applied per file.
2309
+ Returns:
2310
+ report: a list of font data results.
2311
+ """
2312
+ report = []
2313
+ all_file_data = get_styles_by_html_files(project_dir)
2314
+ for file in all_file_data:
2315
+ number_of_fonts = 0
2316
+ filename = clerk.get_file_name(file.get("file"))
2317
+ stylesheets = file.get("stylesheets")
2318
+ font_styles = []
2319
+ for sheet in stylesheets:
2320
+ font_details = get_font_families(sheet)
2321
+ number_of_fonts += len(font_details)
2322
+ for item in font_details:
2323
+ selector = item.get("selector")
2324
+ family = item.get("family")
2325
+ if "," in family:
2326
+ first_font = family.split(",")[0].strip()
2327
+ else:
2328
+ first_font = family
2329
+ first_font = first_font.replace("'", "")
2330
+ first_font = first_font.replace('"', "")
2331
+ if first_font.lower() == "times new roman":
2332
+ results = f"fail: {filename}: {selector} element was set "
2333
+ results += "to the default font"
2334
+ elif number_of_fonts >= min and number_of_fonts <= max:
2335
+ results = f"pass: {filename}: {selector} element "
2336
+ results += f"was set to {first_font}"
2337
+ elif number_of_fonts < min:
2338
+ results = f"fail: {filename} did not apply {min} fonts, "
2339
+ results += f"instead, it applied {number_of_fonts} fonts."
2340
+ if results not in font_styles:
2341
+ font_styles.append(results)
2342
+ # We have all font styles applied
2343
+ if not font_styles:
2344
+ report.append(f"fail: {filename} No modified fonts for {filename}")
2345
+ else:
2346
+ report += font_styles
2347
+ if not report:
2348
+ report.append("fail: no html files to apply font styling to")
2349
+ return report
2350
+
2351
+
2352
+ def get_global_color_report(project_dir: str, level="aaa") -> list:
2353
+ """Returns a report on which files in a project apply global colors
2354
+
2355
+ Args:
2356
+ project_dir: the project folder path.
2357
+ level: whether we are testing for Normal AAA or Normal AA
2358
+
2359
+ Returns:
2360
+ report: a list of files and a pass or fail message for each."""
2361
+ report = []
2362
+ all_file_data = get_all_project_stylesheets(project_dir)
2363
+ for data in all_file_data:
2364
+ filename = data[0]
2365
+ passes = []
2366
+ for sheet in data[1]:
2367
+ rules = sheet.rulesets
2368
+ global_color_data = get_global_color_details(rules)
2369
+ if global_color_data:
2370
+ for item in global_color_data:
2371
+ file, result = get_color_data(filename, item, level)
2372
+ passes.append(f"pass: {file} {result}")
2373
+ if passes:
2374
+ details = ""
2375
+ for detail in passes:
2376
+ details += detail
2377
+ else:
2378
+ details = f"fail: {filename} does NOT apply global colors"
2379
+ report.append(details)
2380
+ if not report:
2381
+ report.append("fail: no html files to apply color styles to")
2382
+ return report
2383
+
2384
+
2385
+ def get_color_data(file: str, color_details: dict, level="aaa") -> tuple:
2386
+ """returns the color contrast data on a color.
2387
+
2388
+ pulls out the selector, background color, text color, contrast
2389
+ ratio, and whether it passes color contrast.
2390
+
2391
+ Args:
2392
+ file: just the name of the file (not path).
2393
+ color_details: tuple of full color & bg color details.
2394
+ level: the level of normal text (AAA or AA).
2395
+
2396
+ Returns:
2397
+ color_data: a tuple with filename and results as a string"""
2398
+ selector = color_details.get("selector")
2399
+ bg_color = color_details.get("background-color")
2400
+ color = color_details.get("color")
2401
+ contrast_ratio = color_details.get("contrast_ratio")
2402
+ if level == "aaa":
2403
+ passes = color_details.get("passes_normal_aaa")
2404
+ else:
2405
+ passes = color_details.get("passes_normal_aa")
2406
+ if passes:
2407
+ results = "passes global colors"
2408
+ else:
2409
+ results = f"{selector} {bg_color} and {color} fail with a contrast"
2410
+ results += f" ratio of {contrast_ratio}."
2411
+ color_data = (file, results)
2412
+ return color_data
2413
+
2414
+
2415
+ def get_heading_color_report(project_dir: str) -> list:
2416
+ """Returns a report on which files in a project apply heading colors
2417
+
2418
+ For now, we just want to have at least a color or background color
2419
+ applied.
2420
+
2421
+ Args:
2422
+ project_dir: the project folder path.
2423
+
2424
+ Returns:
2425
+ report: a list of files and a pass or fail message for each."""
2426
+ report = []
2427
+ header_re = regex_patterns.get("header_selector")
2428
+ if project_dir[-1] != "/":
2429
+ project_dir += "/"
2430
+ all_file_data = get_all_project_stylesheets(project_dir)
2431
+ for file in all_file_data:
2432
+ filename = file[0]
2433
+ filepath = project_dir + filename
2434
+ all_color_rules = get_all_color_rules(filepath)
2435
+ header_selectors = []
2436
+ for key, val in all_color_rules.items():
2437
+ if key == "file":
2438
+ continue
2439
+ is_header_selector = re.findall(header_re, key)
2440
+ if is_header_selector:
2441
+ # we have a header selector
2442
+ # we only need to check color
2443
+ color_data = val
2444
+ color_value = color_data.get("color")
2445
+ bg_value = color_data.get("background-color")
2446
+ if color_value:
2447
+ if bg_value:
2448
+ header_selectors.append(
2449
+ (filename, color_value, bg_value)
2450
+ )
2451
+ else:
2452
+ header_selectors.append((filename, color_value, None))
2453
+ if bg_value:
2454
+ header_selectors.append((filename, None, bg_value))
2455
+ if header_selectors:
2456
+ report.append(f"pass: {filename} applies colors to headers")
2457
+ else:
2458
+ report.append(f"fail: {filename} does NOT apply colors to headers")
2459
+ if not report:
2460
+ report.append("fail: no html files to apply header colors to")
2461
+ return report
2462
+
2463
+
2225
2464
  if __name__ == "__main__":
2226
2465
  insane_gradient = """
2227
2466
  -moz-radial-gradient(0% 200%, ellipse cover,
@@ -410,6 +410,30 @@ def add_if_not_in(my_list: list, item: str) -> None:
410
410
  my_list.append(item)
411
411
 
412
412
 
413
+ def get_number_of_elements_per_file(
414
+ project_dir: str, element_data: list
415
+ ) -> list:
416
+ """returns a list of number of elements per file in a project folder.
417
+
418
+ Args:
419
+ project_dir: the folder we want to check
420
+ element_data: a list of tuples of element (str) and required number
421
+ (int)
422
+
423
+ Returns:
424
+ elements_per_file: a list of tuples that includes the file, the
425
+ element, and the number of occurrences.
426
+ """
427
+ elements_per_file = []
428
+ all_html_files = get_all_html_files(project_dir)
429
+ for file in all_html_files:
430
+ for i in range(len(element_data)):
431
+ # Get requirements for exact number
432
+ element, number = element_data[i]
433
+ elements_per_file.append((file, element, number))
434
+ return elements_per_file
435
+
436
+
413
437
  if __name__ == "__main__":
414
438
  file_with_inline_styles = "tests/test_files/sample_with_inline_styles.html"
415
439
  markup = clerk.file_to_string(file_with_inline_styles)
@@ -7,6 +7,8 @@ import requests
7
7
  from bs4 import BeautifulSoup
8
8
  from file_clerk import clerk
9
9
 
10
+ from webcode_tk import html_tools as html
11
+
10
12
  w3cURL = "https://validator.w3.org/nu/?out=json"
11
13
 
12
14
  # Instantiate a stateful browser
@@ -89,7 +91,7 @@ def get_markup_validity(file_path: str) -> list:
89
91
  errors: a list of dictionary types (converted from the JSON
90
92
  response from the validator."""
91
93
  errors = []
92
- payload = open(file_path)
94
+ # payload = open(file_path)
93
95
  with open(file_path, "rb") as payload:
94
96
  headers = {
95
97
  "content-type": "text/html; charset=utf-8",
@@ -253,6 +255,61 @@ def validate_css(css_code: str) -> bs4.ResultSet:
253
255
  return results
254
256
 
255
257
 
258
+ def get_project_validation(project_dir: str, type="html") -> list:
259
+ """returns a report on HTML or CSS validation per HTML file.
260
+
261
+ You choose the project folder and the type (html or css), and it
262
+ will return a list of per-files errors
263
+ """
264
+ report = []
265
+ passing_files = []
266
+ all_files = clerk.get_all_project_files(project_dir)
267
+ for file in all_files:
268
+ errors = []
269
+ file_type = clerk.get_file_type(file)
270
+ filename = clerk.get_file_name(file)
271
+ if type == "html" and file_type == "html":
272
+ errors = get_markup_validity(file)
273
+ if errors:
274
+ report.append(
275
+ f"fail: {filename} has {len(errors)} validation errors."
276
+ )
277
+ else:
278
+ passing_files.append(filename)
279
+ else:
280
+ if file_type == "html" and type == "css":
281
+ style_tag = html.get_elements("style", file)
282
+ if style_tag:
283
+ code = html.get_element_content(style_tag)
284
+ result = validate_css(code)
285
+ errors_list = get_css_errors_list(result)
286
+ if errors_list:
287
+ errors += errors_list
288
+ else:
289
+ passing_files.append(filename)
290
+ if file_type == "css":
291
+ code = clerk.file_to_string(file)
292
+ result = validate_css(code)
293
+ errors_list = get_css_errors_list(result)
294
+ if errors_list:
295
+ errors += errors_list
296
+ else:
297
+ passing_files.append(filename)
298
+ if errors:
299
+ report.append(
300
+ f"fail: {filename} has {len(errors)} css errors."
301
+ )
302
+ if not report:
303
+ if passing_files:
304
+ for passing_file in passing_files:
305
+ msg = f"pass: {passing_file} passes {type.upper()} validation"
306
+ report.append(msg)
307
+ if not passing_files:
308
+ report.append("fail: no files present to validate")
309
+ # TODO - make sure this covers all scenarios
310
+ return report
311
+
312
+
256
313
  if __name__ == "__main__":
257
314
  path = "tests/test_files/sample_with_errors.html"
258
315
  report = get_markup_validity(path)
File without changes
File without changes