CogniSkill · Python Foundations for IT Professionals

Content review copy. This does not replace the site theme. For SC-300, this preview shows additions; the existing teaching is retained on import.

Course outcome

You will build a command-line inventory report that reads a CSV file, validates each row, classifies capacity, writes a separate report, and explains rejected data. You will also build a small independent test suite. The programme begins with running a file and works through values, decisions, collections, loops, functions, exceptions and files. It does not require a cloud account or administrator privileges.

The central project is deliberately modest. A beginner needs to understand every decision in a small working programme before adding network access or automated changes. By the end, you should be able to adapt the input and classification rule without copying a whole new script from an assistant.

Keep a practice folder containing your source, synthetic inputs, output reports and notes. The input files in this course contain fictional host names. Make a prediction before each exercise, then compare it with the observed result. Unexpected results are useful evidence about your mental model.

Four chapters, with practical work

  1. Run a programme and understand its values
  2. Make decisions and work with collections
  3. Read CSV safely and build an auditable pipeline
  4. Test, explain and extend your report

Applied capability and learning route

Implement and test a pure data-validation function with explicit failure results.

Follow the lessons in order: establish the model, trace the supplied evidence, attempt the case, compare the worked solution, and complete the changed-condition project. The target is intermediate reasoning within this stated scope, not mastery of the entire profession or product. If a prerequisite is unfamiliar, pause and use the linked foundational teaching before attempting the project.

All teaching, explained answers and core synthetic practice are free. Some operational labs require separately available software, a licensed test tenant or an authorized lab; offline completion does not claim hands-on product proficiency.

How to complete this course

Study the worked examples, attempt the exercises before opening solutions, and submit your own project notes. Completion records are self-reported; practical competence requires the stated verification.

Run a programme and understand its values

Your environment and first execution

Install a supported Python 3 interpreter from the official Python distribution or your approved package manager. On some systems the command is python; on others it is python3. Run the relevant command with --version and record the result. The examples use the standard library; installing a package manager or cloud SDK is unnecessary for the core course.

Create an empty practice directory and open a terminal there. Save a file named hello.py containing print("Inventory practice"). Run python hello.py or python3 hello.py. The expected output is one line: Inventory practice. If the terminal says it cannot open the file, inspect the working directory and filename. If it says the command is unknown, inspect interpreter installation and command selection. Those are different failures.

An interactive prompt executes statements as you enter them. A script stores statements in a file and executes them in order. You can explore a small expression interactively, but put the lab programme in a file so you can reproduce it. Do not paste the prompt marker >>> into a script; that marker belongs to the documentation's presentation.

A Python file is text, not a Word document. Be careful that an editor has not saved hello.py.txt. A visible file label may conceal an extension depending on operating-system settings. Record the interpreter command and actual file path in your notes so another learner can reproduce your run.

Names, values and types

Assignment binds a name to a value. In threshold = 10, the name is threshold and the value is an integer. The equals sign here performs assignment; it is not an equality test. The expression threshold == 10 evaluates whether two values compare equal. Confusing the two operations creates either a syntax error or the wrong logic, depending on the context.

host = "LAB-A"
free_text = "9"
threshold = 10
print(host)
print(type(free_text).__name__)
print(type(threshold).__name__)
print(free_text + "1")
print(int(free_text) + 1)

The outputs are LAB-A, str, int, 91 and 10. The string operation joins characters; it does not add numbers. CSV fields will arrive as strings, so this distinction is central to the report you are building. A number-looking value is not necessarily already a numeric value.

Reassigning threshold later changes what that name refers to. It does not rewrite every earlier calculation. For example, calculate limit_plus_one = threshold + 1, then set threshold = 20. limit_plus_one remains 11 because its assignment already evaluated the expression. A name is not a spreadsheet cell with an automatically recalculated formula.

Choose descriptive names such as free_pct and invalid_count. Short names can be useful in very small expressions, but a reporting programme benefits from names describing meaning and units. free_pct is different from free_gb; comparing a percentage to a threshold expressed in gigabytes is a requirements error even when the code runs.

Strings, formatting and exact observations

A string is a sequence of characters. Quoting marks its boundaries in source code. A printed value does not normally include the source quotes. Leading and trailing spaces can be part of the value even when they are difficult to see in a terminal.

raw_host = " LAB-A "
clean_host = raw_host.strip()
print(repr(raw_host))
print(repr(clean_host))
print(f"Host: {clean_host}; threshold: {10}%")

The first two lines display the difference between the raw and cleaned strings. repr() is helpful when diagnosing whitespace because it gives a representation of the value rather than pretending spaces are irrelevant. strip() returns a new string; it does not mutate the original string.

Use an f-string for readable presentation. It evaluates expressions inside braces and produces a string. Presentation should come after calculation. If your only output is a formatted sentence, another programme may have to parse that sentence to reuse the data. A structured dictionary or CSV report is usually a better boundary between calculation and presentation.

Lab: distinguish valid data from an absent measurement

Predict the result of int("9"), int("0"), int("unknown") and int("") before testing. The first two conversions succeed. The latter two raise ValueError. An unknown measurement is not zero; zero is an actual number. A report that substitutes zero for every conversion failure can wrongly tell an operator that all affected disks are full.

Now test int("9.5"). It also fails. Decide whether the specification permits decimal percentages before changing the programme to float(). A conversion rule is part of the data contract. If decimals are supported, test boundary values such as 9.9 and 10.0, and think about non-finite values separately. Do not quietly change the input contract because one example was inconvenient.

Check your understanding

Write a short explanation of why "9" + "1" produces "91". Then state what int("9") + 1 produces and where the conversion occurs. Explain why a missing measurement cannot safely become zero. If your explanation relies only on “Python knows what I mean,” revisit the types shown by type().

Worked answer: the first expression joins two strings. The second converts the first string to an integer before arithmetic. A missing measurement contains insufficient evidence for a numeric claim. The eventual report must preserve that distinction with an invalid or missing-data classification.

Reasoning model

Separate parsing, validation, calculation and presentation. A pure function receives data and returns a result without reading files or changing external state, making boundary tests inexpensive. Catch the narrow failure you expect rather than hiding every exception. Decide whether invalid rows stop the whole job or become structured rejections; either policy must preserve a visible audit of what was excluded.

Official reference for further study

Make decisions and work with collections

Translate the requirement before coding

The report's rule is: a valid percentage strictly below ten requires review. A valid percentage is an integer between zero and one hundred, inclusive. Exactly ten is OK. An invalid value must be reported separately. Write these statements next to your test cases before choosing an operator.

An if statement selects a branch using a condition. elif checks another condition when earlier branches did not match. else handles the remaining path. Indentation identifies which statements belong to a branch. Use consistent spaces and let your editor help; visual alignment that mixes tabs and spaces can be misleading.

free_pct = 10
if not 0 <= free_pct <= 100:
    status = "INVALID"
elif free_pct < 10:
    status = "REVIEW"
else:
    status = "OK"
print(status)

At ten the output is OK. At nine it is REVIEW. At 101 it is INVALID. The invalid check comes first so an impossible value cannot be classified as healthy just because it is greater than ten. A syntax-valid programme can still encode the wrong requirement.

Deliberately replace < 10 with <= 10. Nine and eleven still behave plausibly; ten changes to REVIEW. That demonstrates why a boundary test is more informative than two comfortable normal cases. Restore the correct condition and keep the ten-percent case in your test suite.

Lists hold a sequence; dictionaries name fields

A list stores ordered values. A dictionary associates keys with values. Neither structure automatically validates the business meaning of its contents. For an inventory row, named fields are clearer than unexplained positions.

rows = [
    {"host": "LAB-A", "free_pct": "9"},
    {"host": "LAB-B", "free_pct": "10"},
]
print(rows[0]["host"])
print(len(rows))

The first output is LAB-A and the second is 2. Index zero selects the first list item; the host key selects the named field inside that dictionary. A list index tells you where a record is in this input, not which device it represents. Later, an inventory reconciliation should use a stable identifier rather than matching the first expected row to the first observed row.

Accessing a missing key with square brackets raises KeyError. Using get() can be appropriate when absence is an allowed state, but it can also hide a broken schema if you turn every absent field into a harmless default. Distinguish a required column from an optional column before deciding how to read it.

A loop repeats a defined operation

A for loop processes items from an iterable. In the report you will perform the same validation for each input row. The loop does not decide whether all rows are trustworthy. That responsibility belongs to the validation function.

for row in rows:
    print(row["host"], row["free_pct"])

This prints two lines. The loop variable refers to the current dictionary during each iteration. It is not a special copy of every field. If you mutate row, you can also change the dictionary inside rows. That matters when you need to retain original evidence.

Create a new output dictionary rather than adding a status directly to the original input row. Keeping source and output separate makes it possible to verify that the report did not alter the received measurements. It also makes rejected-row review more useful because the original text is still available.

Build a function with one reviewable purpose

A function accepts inputs and returns a result. The caller decides what to do with that result. A pure classification function should not read files, print a table, reset a device or write to a database. Separating those actions reduces the number of things you must understand when a test fails.

def classify_number(free_pct):
    if isinstance(free_pct, bool) or not isinstance(free_pct, int):
        return "INVALID"
    if not 0 <= free_pct <= 100:
        return "INVALID"
    return "REVIEW" if free_pct < 10 else "OK"

assert classify_number(9) == "REVIEW"
assert classify_number(10) == "OK"
assert classify_number(0) == "REVIEW"
assert classify_number(101) == "INVALID"
assert classify_number(True) == "INVALID"

The Boolean check is intentional: in Python, bool is a subtype of int, but True is not a valid disk measurement under this contract. Code behavior and business meaning are related but not identical. You need a domain rule to distinguish them.

return sends a result back to the caller and exits that function call. print() displays text but does not substitute for returning a reusable result. If a function prints REVIEW and returns nothing, its caller receives None. That can break an aggregation even when the terminal looked correct.

Lab: extend the classification without changing its meaning

Add test cases for -1, 100 and the string "9". Under this function's numeric contract, -1 and "9" are INVALID and 100 is OK. The CSV reader will use a separate conversion step before calling numeric classification. Keeping conversion separate helps you explain whether a failure concerns source text or a valid number outside the permitted range.

Now add a reason to the result. A valid implementation can return {"status": "INVALID", "reason": "not an integer measurement"} for True and a different reason for 101. Do not return a contradictory result such as status OK with reason “out of range.” Test the whole output object when its fields form an acceptance contract.

Evidence laboratory — attempt before reading the solution

Input rows use strings: (A,"100","9"), (B,"200","20"), (C,"0","0"), (D,"100","unknown"). Fields are name,total,free. A valid row requires total>0 and 0<=free<=total. Alert when free/total is strictly below 0.10. Return valid results separately from rejected rows with reasons. Predict outputs, then implement without network access. Do not treat conversion failure as the numeric value zero.

Official reference for further study

Read CSV safely and build an auditable pipeline

File operations have their own failure modes

A file can be absent, inaccessible, empty or structurally wrong. A row inside a valid file can contain an invalid value. Those are distinct states. If a whole file cannot be read, reporting zero devices would turn a collection failure into a false inventory claim.

Use with to scope an open file so it is closed when the block finishes. Give the source encoding explicitly. For CSV files opened with Python's CSV module, use newline="" so the module can handle newline conventions correctly. A source file path should be supplied deliberately; do not assume every scheduled process uses your interactive working directory.

csv.DictReader reads column headings and supplies each row as a dictionary. It does not automatically convert percentage text to integers. Validate required headings before classifying rows. A misspelled free_pct heading is a file-contract failure, not a row whose capacity happens to be unknown.

Complete project implementation

Save this as inventory_report.py. It contains no network access and no device remediation. The default operation produces a separate CSV report from the fictional input you supply.

import argparse
import csv
from collections import Counter
from pathlib import Path

REQUIRED = {"host", "free_pct"}


def classify_row(row):
    original_host = row.get("host")
    raw = row.get("free_pct")
    host = original_host.strip() if isinstance(original_host, str) else ""
    if not host:
        return {"host": host, "source_value": raw, "free_pct": None,
                "status": "INVALID", "reason": "missing host"}
    try:
        if not isinstance(raw, str):
            raise ValueError("missing or non-text value")
        free = int(raw)
    except (ValueError, TypeError):
        return {"host": host, "source_value": raw, "free_pct": None,
                "status": "INVALID", "reason": "not an integer"}
    if not 0 <= free <= 100:
        return {"host": host, "source_value": raw, "free_pct": None,
                "status": "INVALID", "reason": "outside 0..100"}
    return {"host": host, "source_value": raw, "free_pct": free,
            "status": "REVIEW" if free < 10 else "OK", "reason": "valid measurement"}


def read_report(path):
    with path.open("r", encoding="utf-8-sig", newline="") as handle:
        reader = csv.DictReader(handle)
        fields = reader.fieldnames or []
        if len(fields) != len(set(fields)):
            raise ValueError("duplicate CSV headings")
        if not REQUIRED.issubset(fields):
            raise ValueError("required headings: host, free_pct")
        output = []
        for number, row in enumerate(reader, start=2):
            if None in row:
                raise ValueError(f"extra CSV fields on line {number}")
            result = classify_row(row)
            result["line"] = number
            output.append(result)
        if not output:
            raise ValueError("input has headings but no data rows")
    return output


def write_report(path, rows):
    # Exclusive creation prevents silent replacement of a previous result.
    with path.open("x", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=["line", "host", "source_value",
                               "free_pct", "status", "reason"])
        writer.writeheader()
        writer.writerows(rows)


def main():
    parser = argparse.ArgumentParser(description="Read-only inventory classification")
    parser.add_argument("source", type=Path)
    parser.add_argument("report", type=Path)
    args = parser.parse_args()
    if args.source.resolve() == args.report.resolve():
        parser.error("source and report must be different files")
    try:
        rows = read_report(args.source)
        write_report(args.report, rows)
    except (OSError, ValueError) as error:
        parser.exit(2, f"Report failed: {error}\n")
    counts = Counter(row["status"] for row in rows)
    print(f"Rows={len(rows)}; INVALID={counts['INVALID']}; "
          f"REVIEW={counts['REVIEW']}; OK={counts['OK']}")


if __name__ == "__main__":
    main()

Create inventory.csv with a header host,free_pct and five rows: LAB-A,9; LAB-B,10; LAB-C,0; LAB-D,unknown; LAB-E,101. Each row goes on its own line. Run python inventory_report.py inventory.csv report.csv. The expected summary is Rows=5; INVALID=2; REVIEW=2; OK=1. Read the generated report and compare each field with the original input.

Trace the boundaries of this design

The reader validates the file structure, then passes one row at a time to classification. The classifier preserves the received value and returns a new result. The writer turns results into a portable table. The command-line boundary reports operational failures using exit status 2 rather than presenting a partial run as success.

utf-8-sig accepts a UTF-8 file with or without a byte-order mark at the start. It is not a universal encoding detector. If your real source uses another encoding, identify it from the source contract and test it deliberately. Do not silence decoding errors by discarding arbitrary bytes.

The programme accepts integer text. Python's int() permits some surrounding whitespace and signs. That is an explicit behavior to review when defining a stricter source format. The programme does not establish that a reported percentage is a fresh measurement from a real device. Source age and collection completeness belong to a later operational integration.

The writer creates a new output file exclusively. If report.csv exists, the run fails instead of overwriting it. Use a new output filename or intentionally remove only a disposable practice report after preserving evidence. Exclusive creation avoids one class of accidental loss, but it is not a transactional guarantee for disk failures during writing: a partial newly created report can remain. Successful completion is established by the exit status and reconciliation, not merely the file's existence.

Six failure experiments

  1. Change LAB-B to 9. The row becomes REVIEW and the summary changes to REVIEW=3, OK=0, INVALID=2. The row count stays five.
  2. Change LAB-D to an empty field. It remains INVALID with a conversion reason; it does not become a measurement of zero.
  3. Remove the free_pct header. The programme exits 2 before writing a normal report. That is a structural failure affecting the whole file.
  4. Add a third field to one row without a matching heading. The reader detects the extra fields and stops. Silently ignoring the extra value would conceal a malformed source.
  5. Use the source path as the output path. Argument validation rejects it before reading/writing a replacement report.
  6. Run against an already existing report filename. Exclusive creation raises a visible failure. The old report remains intact.

Keep the input and result for every experiment in separate files. A test is not simply “I ran the programme.” It states the change, expected behavior, observed behavior and explanation. A reviewer should be able to determine whether the requirement or implementation needs correction.

File and identity limitations

The source format may contain duplicate host names. This classifier preserves each row; it does not claim to reconcile a unique device inventory. If your later task requires one row per device, add a stable identity contract and a duplicate-key check. Do not silently keep the final row in a dictionary and call that validation.

A host label is not necessarily a globally unique identifier. Case normalization and trimming solve presentation differences only when the source contract defines those differences as equivalent. Stripping arbitrary punctuation can merge two distinct devices. Use the Python IT Automation course to develop that reconciliation task separately.

Worked laboratory solution

Compare your reasoning with the worked solution

A is valid with ratio 0.09 and alerts. B is valid at 0.10 and does not alert. C violates positive total before division; D fails numeric parsing. Therefore there are two valid results and two rejected rows. Use numeric conversion inside a narrow handling boundary, then domain checks. A broad exception handler returning “OK” can hide both bad input and programming bugs. Preserve the row identifier in each rejection, but avoid logging sensitive payloads in real projects.

Official reference for further study

Workbook already downloaded

Test, explain and extend your report

Independent expectations

A test should express the requirement rather than a second copy of the implementation. If you use the same incorrect <= 10 condition to create your expected results, both code paths can agree while violating “strictly below ten.” Write an explicit table of cases from the specification.

Save this as test_inventory_report.py beside the implementation and run python -m unittest -v. Do not paste these tests into the programme's command-line execution block; they are a separate consumer of its classification function.

import unittest
from inventory_report import classify_row


class ClassificationTests(unittest.TestCase):
    def test_valid_boundaries(self):
        cases = [("0", "REVIEW"), ("9", "REVIEW"),
                 ("10", "OK"), ("100", "OK")]
        for raw, expected in cases:
            with self.subTest(raw=raw):
                result = classify_row({"host": "LAB-A", "free_pct": raw})
                self.assertEqual(result["status"], expected)
                self.assertEqual(result["free_pct"], int(raw))

    def test_invalid_values(self):
        for raw in ["", "unknown", "-1", "101", "9.5", None, True]:
            with self.subTest(raw=raw):
                result = classify_row({"host": "LAB-A", "free_pct": raw})
                self.assertEqual(result["status"], "INVALID")
                self.assertIsNone(result["free_pct"])

    def test_source_is_not_changed(self):
        source = {"host": " LAB-A ", "free_pct": "9"}
        before = source.copy()
        result = classify_row(source)
        self.assertEqual(source, before)
        self.assertEqual(result["host"], "LAB-A")

    def test_missing_identity(self):
        result = classify_row({"host": "   ", "free_pct": "20"})
        self.assertEqual(result["status"], "INVALID")
        self.assertEqual(result["reason"], "missing host")


if __name__ == "__main__":
    unittest.main()

These tests cover classification, not the whole operational pipeline. You still need the file experiments from the previous chapter. Passing a pure-function test cannot establish that the command opens the intended path, handles encoding correctly or refuses an existing output file.

A useful failure investigation

When a test fails, read the expected and actual values. Identify the smallest relevant function and case. A boundary failure suggests a comparison problem; every valid row failing suggests an input-contract or parsing problem; a missing output suggests file or command-line behavior. Do not solve every failure by catching a broader exception.

Temporarily make the comparison wrong again and run the tests. The ten-percent subtest must fail. Restore the operator and rerun. A test suite that never fails under a deliberate defect provides weak evidence that it protects the intended behavior.

assert statements are helpful in small teaching examples, but Python can disable ordinary asserts when run with optimization options. The unittest assertions are explicit test methods. Production input validation should use deliberate checks and errors, rather than relying only on an assert to reject untrusted input.

Project extension A: report data quality

Add a summary file containing received row count, valid row count, invalid row count and status counts. Decide what to do if the summary filename already exists. Reconcile the totals: valid plus invalid must equal received, and the status totals must also equal received.

A complete answer preserves all five baseline rows, identifies two invalid values, and does not pretend invalid rows are healthy. Include the source filename and an observation timestamp for the report run, while explaining that this timestamp is not a device measurement timestamp. If you add source timestamps later, keep the two concepts separate.

Project extension B: configurable threshold

Allow a threshold argument with a default of ten. Define the accepted threshold range and whether it must be an integer. Pass that value into classification rather than reading a mutable global from multiple functions. Test thresholds zero, ten and one hundred, and reject an out-of-range setting before writing a report.

At threshold zero, a valid zero-percent value is OK under “strictly below threshold.” That may be mathematically consistent but operationally unhelpful. Discuss whether the business should allow zero as a configured threshold. Software correctness includes both faithful implementation and a useful requirement; a passing test cannot make a poor policy wise.

Project extension C: separate collection from remediation

Write a proposal for a later live inventory collector. Identify the source, required permissions, collection-completeness signal, source age and failure behavior. Keep remediation out of this course's report programme. An invalid field, a failed collector and a genuine low-capacity measurement require different responses.

A complete proposal does not claim that a missing host should be deleted or that every REVIEW row should trigger an automatic cleanup. It names the accountable owner and evidence needed before a change. This boundary keeps a beginner's reporting exercise from accidentally becoming an unsafe administration script.

Final acceptance record

Submit your source, test file, baseline input and report, six failure experiments and one project extension. Include the interpreter version and commands used. Explain the strict boundary, invalid-versus-zero distinction, source preservation and output-file policy in your own words.

The acceptance criteria are observable: expected baseline labels, retained invalid rows, unchanged input, correct exit status for structural failures, visible rejection of an existing output, and a deliberate defect caught by a test. Reading the solution and clicking Complete is not an independently assessed demonstration of these skills.

Suggested next study

Choose Python IT Automation if you want to reconcile datasets or Python Security Log Analysis if you want to work with timestamps and aggregation. You now have the prerequisite ideas: parsing, explicit contracts, pure functions, iteration, separate output and independent expected results. Reuse those ideas rather than treating the next script as unrelated magic.

Transfer project — changed conditions

Add negative values, free greater than total, leading/trailing whitespace and non-finite numeric strings. Python float can represent NaN/infinity, so define and test finiteness explicitly if you accept floats. Write tests for exact threshold and near-threshold values. Explain whether your use case needs decimal arithmetic, and keep that decision separate from output rounding. Submit code, test output and a short contract for accepted inputs.

Practical assessment rubric

Score each dimension 0–2: 0 = absent or materially incorrect; 1 = partly correct with an unresolved gap; 2 = correct, explicit and supported by the supplied evidence or an actual labeled lab result.

  1. Evidence/model: identify the relevant entities, inputs, assumptions and boundaries without inventing facts.
  2. Main result: derive the case-specific expected result, calculation or decision and show the intermediate reasoning.
  3. Failure/boundary: test the changed condition and explain why the original conclusion does or does not transfer.
  4. Verification: supply reproducible steps and expected/observed results; label manual traces as manual, not executed.
  5. Communication/safety: explain limitations, owners and a proportionate next action without unsafe or unnecessary access.

Target: at least 8/10, with no zero in main result or communication/safety. This is a local learning rubric, not an accredited certification standard. A written/manual solution demonstrates reasoning only; operational competence additionally requires the relevant executable lab in an authorized environment. The native multiple-choice score is separate and does not automatically grade this rubric.

Submission template

Remediation

If the main result is wrong, reconstruct the supplied case from the raw inputs before changing code or controls. If the boundary case is wrong, identify the assumption that changed and rerun both the original and changed case. If verification is missing, do not convert a prediction into an observed result. Reattempt the transfer task without copying the worked solution, then explain it to another person.

Official reference for further study

Assessment: explain your decisions

Choose an answer and write your reason before opening each explanation. Then explain why the other choices do not fit the evidence. Use missed questions to revisit the lesson. This knowledge check is only one part of assessment: complete the practical transfer project as well. It does not predict certification-exam success.

1. Which device matches the original condition?

  1. LAB-A only
  2. Both devices
  3. Neither device
Answer and explanation

LAB-A only — 9 < 10, but 10 < 10 is false.

2. Why return results from a function?

  1. To require admin access
  2. To separate calculation from display
  3. To remove all tests
Answer and explanation

To separate calculation from display — Returned values can be tested and reused independently.

3. Which test is especially useful for threshold logic?

  1. Only a typical value
  2. Only file size
  3. A value exactly at the boundary
Answer and explanation

A value exactly at the boundary — Equality reveals < versus <= mistakes.

4. The rule flags free capacity below 10%. A valid row has free_pct 10. Which result fits the rule?

  1. OK, because exactly 10 is outside the strict below-10 condition.
  2. REVIEW, because 10 is close to the threshold.
  3. INVALID, because a threshold value cannot be classified.
Answer and explanation

OK, because exactly 10 is outside the strict below-10 condition. — A strict boundary is a requirement, not a judgement about closeness. Test 9, 10 and 11 independently. A change from < to <= incorrectly flags the boundary row.

5. A CSV capacity field contains unknown. Which reporting behavior preserves the evidence?

  1. Replace unknown with zero so the field stays numeric.
  2. Mark the row invalid and retain the raw source value and reason.
  3. Drop the row and call the remaining population healthy.
Answer and explanation

Mark the row invalid and retain the raw source value and reason. — Unknown is not measured zero. Conversion or silent deletion creates an unsupported conclusion. The report keeps a rejected value visible so the collector and analyst can investigate it.

6. The report output already exists. What should this course program do?

  1. Overwrite it because the new report is likely more current.
  2. Append new rows without checking the schema.
  3. Fail clearly and preserve the existing report.
Answer and explanation

Fail clearly and preserve the existing report. — The no-overwrite contract protects evidence. Use a new output path. A preflight existence check alone can race; the exclusive file-open operation also needs to enforce the contract.

7. How many fixture rows are valid?

  1. Two.
  2. Four because every row has three strings.
  3. Three because unknown should become zero.
Answer and explanation

Two. — Shape alone is insufficient. C violates the positive-total rule; D cannot be parsed as the required number.

8. Should B alert?

  1. It cannot be calculated.
  2. No; its ratio is exactly 0.10.
  3. Yes; the rule says less than or equal.
Answer and explanation

No; its ratio is exactly 0.10. — 20/200 = 0.10 and the rule is strictly below the threshold.

9. Why add a finiteness check when accepting float input?

  1. Every float is necessarily a whole number.
  2. It replaces all other validation.
  3. NaN/infinity may parse numerically but violate the intended domain.
Answer and explanation

NaN/infinity may parse numerically but violate the intended domain. — Successful parsing does not imply a sensible capacity value. Finiteness and range checks are separate requirements.

Use the explained questions above for offline practice. A scored attempt requires the course assessment on CogniSkill.ca.