from pathlib import Path

r_code = r'''# ============================================================
```{r}
# Smart Warehouse Inventory Simulation
# File: warehouse_manipulation.r
##  Name                 : Sana Khan
#  Student Number       : 2239157
#  Module Code          : ACCA7022
#  Assessment Title     : Smart Warehouse Inventory Simulation
#  Script Name          : warehouse_manipulation.r
#  Date                 : 23rd October 2026
```

# ============================================================

```{r}
# This script generates warehouse data from scratch and uses loops
# to calculate zone totals, category averages, anomalies, and peak days.
# No apply-family functions, aggregation functions, dplyr, or user-defined
# functions are used.
```


# ------------------------------
```{r}
# Setup: simulation parameters
```

# ------------------------------

```{r}
set.seed(12345)  # Makes the random variation reproducible.

```

```{r}
NumberOfDays <- 28
NumberOfZones <- 5
NumberOfCategories <- 6
BaseUnits <- 100
TimePerUnit <- 0.85

```

# ============================================================
```{r}
# TASK A: Generate Warehouse Data
```

# ============================================================
```{r}
# There is one record for every Day-Zone-Category combination:
# 28 days * 5 zones * 6 categories = 840 records.
#
# UnitsProcessed = BaseUnits + (Zone * 8) + (Category * 5)
#                  + RandomVariation
# RandomVariation is an integer between -12 and +12.
# ProcessingTime = UnitsProcessed * TimePerUnit.

```

```{r}
NumberOfRecords <- NumberOfDays * NumberOfZones * NumberOfCategories

```

```{r}
warehouse_data <- data.frame(
  Day = integer(NumberOfRecords),
  Zone = integer(NumberOfRecords),
  Category = integer(NumberOfRecords),
  UnitsProcessed = integer(NumberOfRecords),
  ProcessingTime = numeric(NumberOfRecords)
)

record_index <- 1

for (day in 1:NumberOfDays) {
  for (zone in 1:NumberOfZones) {
    for (category in 1:NumberOfCategories) {
      RandomVariation <- sample(-12:12, size = 1)

      UnitsProcessed <- BaseUnits + (zone * 8) +
        (category * 5) + RandomVariation

      ProcessingTime <- UnitsProcessed * TimePerUnit

      warehouse_data$Day[record_index] <- day
      warehouse_data$Zone[record_index] <- zone
      warehouse_data$Category[record_index] <- category
      warehouse_data$UnitsProcessed[record_index] <- UnitsProcessed
      warehouse_data$ProcessingTime[record_index] <- ProcessingTime

      record_index <- record_index + 1
    }
  }
}
```


```{r}
cat("\nTASK A: First 10 rows of warehouse_data\n")
print(head(warehouse_data, 10))

cat("\nTASK A: Dataset dimensions\n")
cat("Number of rows:", nrow(warehouse_data), "\n")
cat("Number of columns:", ncol(warehouse_data), "\n")

```

```{r}
# Expected dimensions: 840 rows and 5 columns.
# UnitsProcessed is explicitly calculated and stored in the dataframe.
```


# ============================================================
```{r}
# TASK B: Total Units Processed per Zone
```

# ============================================================
```{r}
# For each zone, loop through every row and add UnitsProcessed
# when the row belongs to that zone.
```


```{r}
zone_totals <- data.frame(
  Zone = 1:NumberOfZones,
  TotalUnits = numeric(NumberOfZones)
)

for (zone in 1:NumberOfZones) {
  total_units <- 0

  for (i in 1:nrow(warehouse_data)) {
    if (warehouse_data$Zone[i] == zone) {
      total_units <- total_units + warehouse_data$UnitsProcessed[i]
    }
  }

  zone_totals$TotalUnits[zone] <- total_units
}

cat("\nTASK B: Total units processed per zone\n")
print(zone_totals)

```

```{r}
# Finding: zone_totals contains one row per warehouse zone and the
# total units processed across all 28 days and all 6 categories.
```


# ============================================================
```{r}
# TASK C: Average Processing Time per Product Category
```

# ============================================================
```{r}
# For each category, loops calculate both the total ProcessingTime
# and the number of records for that category. The average is total
# processing time divided by the number of matching records.
```


```{r}
category_average_time <- data.frame(
  Category = 1:NumberOfCategories,
  AverageProcessingTime = numeric(NumberOfCategories)
)

for (category in 1:NumberOfCategories) {
  total_processing_time <- 0
  matching_records <- 0

  for (i in 1:nrow(warehouse_data)) {
    if (warehouse_data$Category[i] == category) {
      total_processing_time <- total_processing_time +
        warehouse_data$ProcessingTime[i]
      matching_records <- matching_records + 1
    }
  }

  if (matching_records > 0) {
    category_average_time$AverageProcessingTime[category] <-
      total_processing_time / matching_records
  }
}

category_average_time$AverageProcessingTime <-
  round(category_average_time$AverageProcessingTime, 2)

cat("\nTASK C: Average processing time per category\n")
print(category_average_time)
```

```{r}

# Finding: each category should have 140 matching records
# (28 days * 5 zones). The table reports its mean processing time.
```


# ============================================================
```{r}
# TASK D: Identify Stock Processing Anomalies
```

# ============================================================
```{r}
# First, calculate the average UnitsProcessed for every Day-Zone
# combination across the six categories. Then compare each record
# with that average. An anomaly is more than 15% above or below it.
```


```{r}
DailyZoneAverage <- numeric(NumberOfRecords)
```


```{r}
for (day in 1:NumberOfDays) {
  for (zone in 1:NumberOfZones) {
    daily_zone_total <- 0
    matching_records <- 0
    
    for (i in 1:nrow(warehouse_data)) {
      if (warehouse_data$Day[i] == day &&
          warehouse_data$Zone[i] == zone) {
        daily_zone_total <- daily_zone_total +
          warehouse_data$UnitsProcessed[i]
        matching_records <- matching_records + 1
      }
    }

    if (matching_records > 0) {
      daily_zone_average <- daily_zone_total / matching_records

      for (i in 1:nrow(warehouse_data)) {
        if (warehouse_data$Day[i] == day &&
            warehouse_data$Zone[i] == zone) {
          DailyZoneAverage[i] <- daily_zone_average
        }
      }
    }
  }
}
```



```{r}
processing_anomalies <- data.frame(
  Day = warehouse_data$Day,
  Zone = warehouse_data$Zone,
  Category = warehouse_data$Category,
  UnitsProcessed = warehouse_data$UnitsProcessed,
  DailyZoneAverage = DailyZoneAverage,
  Anomaly = logical(NumberOfRecords)
)

for (i in 1:nrow(warehouse_data)) {
  if (processing_anomalies$UnitsProcessed[i] >
      processing_anomalies$DailyZoneAverage[i] * 1.15 ||
      processing_anomalies$UnitsProcessed[i] <
      processing_anomalies$DailyZoneAverage[i] * 0.85) {
    processing_anomalies$Anomaly[i] <- TRUE
  } else {
    processing_anomalies$Anomaly[i] <- FALSE
  }
}

true_anomalies <- processing_anomalies[
  processing_anomalies$Anomaly == TRUE, ]
```


```{r}
cat("\nTASK D: First 10 records where Anomaly is TRUE\n")
if (nrow(true_anomalies) > 0) {
  print(head(true_anomalies, 10))
} else {
  cat("No anomalies were identified using the specified 15% threshold.\n")
}

cat("Total anomalies identified:", nrow(true_anomalies), "\n")
```


```{r}
# Note: processing_anomalies retains all 840 records and labels each
# record TRUE or FALSE. true_anomalies contains only flagged records.
```


# ============================================================
```{r}
# TASK E: Most Productive Day for Each Zone
```

# ============================================================
```{r}
# For every zone and day, loop through the dataset to calculate that
# day's total units. Keep the day with the highest total for each zone.
```


```{r}
peak_zone_days <- data.frame(
  Zone = integer(NumberOfZones),
  Day = integer(NumberOfZones),
  TotalUnits = numeric(NumberOfZones)
)

for (zone in 1:NumberOfZones) {
  highest_total <- -Inf
  highest_day <- NA_integer_

  for (day in 1:NumberOfDays) {
    daily_total <- 0

    for (i in 1:nrow(warehouse_data)) {
      if (warehouse_data$Zone[i] == zone &&
          warehouse_data$Day[i] == day) {
        daily_total <- daily_total + warehouse_data$UnitsProcessed[i]
      }
    }

    if (daily_total > highest_total) {
      highest_total <- daily_total
      highest_day <- day
    }
  }

  peak_zone_days$Zone[zone] <- zone
  peak_zone_days$Day[zone] <- highest_day
  peak_zone_days$TotalUnits[zone] <- highest_total
}

```

```{r}
cat("\nTASK E: Most productive day for each zone\n")
print(peak_zone_days)
```


```{r}
# Finding: peak_zone_days contains one row per zone, showing the day
# with the greatest combined UnitsProcessed total across categories.
```


# ============================================================
```{r}
# FINAL VALIDATION
```

# ============================================================
```{r}
# These checks confirm that the dataset has the required structure
# and that the key columns contain no missing values.
```


```{r}
cat("\nFINAL VALIDATION\n")
cat("Expected rows (840):", nrow(warehouse_data), "\n")
cat("Expected columns (5):", ncol(warehouse_data), "\n")
cat("Missing UnitsProcessed values:",
    sum(is.na(warehouse_data$UnitsProcessed)), "\n")
cat("Missing ProcessingTime values:",
    sum(is.na(warehouse_data$ProcessingTime)), "\n")

stopifnot(nrow(warehouse_data) == 840)
stopifnot(ncol(warehouse_data) == 5)
stopifnot(all(warehouse_data$UnitsProcessed ==
  warehouse_data$UnitsProcessed))  # Basic self-consistency check.

cat("\nAll tasks completed.\n")

```


rmd = r'''---
title: "Smart Warehouse Inventory Simulation"
author: "Sana Khan (replace if required)"
date: "15 October 2026"
output: html_document
---

# Assessment details

- **Student number:** [Enter student number]
- **Module code:** [Enter module code]
- **Assessment:** Smart Warehouse Inventory Simulation

This report documents the R code used to generate simulated warehouse data and complete Tasks A–E. The main calculations use loops. No `apply()`, `lapply()`, `sapply()`, `tapply()`, `aggregate()`, `rowSums()`, `colSums()`, `rowMeans()`, `colMeans()`, `dplyr`, `summarise()`, or user-defined functions are used.

## Task A — Generate warehouse data

The simulation covers 28 days, 5 zones and 6 product categories, giving 840 combinations. For every combination, `UnitsProcessed` is calculated using the required formula, including a random integer variation from -12 to +12. `ProcessingTime` is calculated as `UnitsProcessed * 0.85`. The seed makes the random data reproducible.

```{r setup-and-task-a}
set.seed(12345)

NumberOfDays <- 28
NumberOfZones <- 5
NumberOfCategories <- 6
BaseUnits <- 100
TimePerUnit <- 0.85
NumberOfRecords <- NumberOfDays * NumberOfZones * NumberOfCategories

warehouse_data <- data.frame(
  Day = integer(NumberOfRecords),
  Zone = integer(NumberOfRecords),
  Category = integer(NumberOfRecords),
  UnitsProcessed = integer(NumberOfRecords),
  ProcessingTime = numeric(NumberOfRecords)
)

record_index <- 1

for (day in 1:NumberOfDays) {
  for (zone in 1:NumberOfZones) {
    for (category in 1:NumberOfCategories) {
      RandomVariation <- sample(-12:12, size = 1)
      UnitsProcessed <- BaseUnits + (zone * 8) +
        (category * 5) + RandomVariation
      ProcessingTime <- UnitsProcessed * TimePerUnit

      warehouse_data$Day[record_index] <- day
      warehouse_data$Zone[record_index] <- zone
      warehouse_data$Category[record_index] <- category
      warehouse_data$UnitsProcessed[record_index] <- UnitsProcessed
      warehouse_data$ProcessingTime[record_index] <- ProcessingTime
      record_index <- record_index + 1
    }
  }
}

head(warehouse_data, 10)
cat("Rows:", nrow(warehouse_data), "\\n")
cat("Columns:", ncol(warehouse_data), "\\n")
```

**Explanation and expected result:** The three nested loops create one row for each day-zone-category combination. The dataset should contain **840 rows and 5 columns**, including the required `UnitsProcessed` and `ProcessingTime` fields.

## Task B — Total units processed per zone

A loop visits every row for each zone and adds the units from matching records.

```{r task-b}
zone_totals <- data.frame(
  Zone = 1:NumberOfZones,
  TotalUnits = numeric(NumberOfZones)
)

for (zone in 1:NumberOfZones) {
  total_units <- 0
  for (i in 1:nrow(warehouse_data)) {
    if (warehouse_data$Zone[i] == zone) {
      total_units <- total_units + warehouse_data$UnitsProcessed[i]
    }
  }
  zone_totals$TotalUnits[zone] <- total_units
}

zone_totals
```

**Explanation:** `zone_totals` has five rows, one for each zone. `TotalUnits` is the sum of all units processed in that zone across all days and categories.

## Task C — Average processing time per category

The loops calculate both the total processing time and the number of records matching each category. The average is the total divided by the count.

```{r task-c}
category_average_time <- data.frame(
  Category = 1:NumberOfCategories,
  AverageProcessingTime = numeric(NumberOfCategories)
)

for (category in 1:NumberOfCategories) {
  total_processing_time <- 0
  matching_records <- 0

  for (i in 1:nrow(warehouse_data)) {
    if (warehouse_data$Category[i] == category) {
      total_processing_time <- total_processing_time +
        warehouse_data$ProcessingTime[i]
      matching_records <- matching_records + 1
    }
  }

  if (matching_records > 0) {
    category_average_time$AverageProcessingTime[category] <-
      total_processing_time / matching_records
  }
}

category_average_time$AverageProcessingTime <-
  round(category_average_time$AverageProcessingTime, 2)
category_average_time
```

**Explanation:** Each category should have 140 records (28 days × 5 zones). The table reports the average estimated processing time in minutes for each category.

## Task D — Identify stock processing anomalies

For every day-zone combination, the first set of loops calculates the average number of units across the six categories. Each record is then flagged if its units are more than 15% above or below that average.

```{r task-d}
DailyZoneAverage <- numeric(NumberOfRecords)

for (day in 1:NumberOfDays) {
  for (zone in 1:NumberOfZones) {
    daily_zone_total <- 0
    matching_records <- 0

    for (i in 1:nrow(warehouse_data)) {
      if (warehouse_data$Day[i] == day &&
          warehouse_data$Zone[i] == zone) {
        daily_zone_total <- daily_zone_total +
          warehouse_data$UnitsProcessed[i]
        matching_records <- matching_records + 1
      }
    }

    if (matching_records > 0) {
      daily_zone_average <- daily_zone_total / matching_records

      for (i in 1:nrow(warehouse_data)) {
        if (warehouse_data$Day[i] == day &&
            warehouse_data$Zone[i] == zone) {
          DailyZoneAverage[i] <- daily_zone_average
        }
      }
    }
  }
}

processing_anomalies <- data.frame(
  Day = warehouse_data$Day,
  Zone = warehouse_data$Zone,
  Category = warehouse_data$Category,
  UnitsProcessed = warehouse_data$UnitsProcessed,
  DailyZoneAverage = DailyZoneAverage,
  Anomaly = logical(NumberOfRecords)
)

for (i in 1:nrow(warehouse_data)) {
  if (processing_anomalies$UnitsProcessed[i] >
      processing_anomalies$DailyZoneAverage[i] * 1.15 ||
      processing_anomalies$UnitsProcessed[i] <
      processing_anomalies$DailyZoneAverage[i] * 0.85) {
    processing_anomalies$Anomaly[i] <- TRUE
  } else {
    processing_anomalies$Anomaly[i] <- FALSE
  }
}

true_anomalies <- processing_anomalies[
  processing_anomalies$Anomaly == TRUE, ]

if (nrow(true_anomalies) > 0) {
  head(true_anomalies, 10)
} else {
  cat("No anomalies were identified using the specified threshold.\\n")
}
cat("Total anomalies identified:", nrow(true_anomalies), "\\n")
```

**
```{r}
## Task E — Most productive day for each zone
```


For each zone and day, the loops sum units processed across the categories. The code retains the day with the highest total for each zone.

```{r task-e}
peak_zone_days <- data.frame(
  Zone = integer(NumberOfZones),
  Day = integer(NumberOfZones),
  TotalUnits = numeric(NumberOfZones)
)

for (zone in 1:NumberOfZones) {
  highest_total <- -Inf
  highest_day <- NA_integer_

  for (day in 1:NumberOfDays) {
    daily_total <- 0

    for (i in 1:nrow(warehouse_data)) {
      if (warehouse_data$Zone[i] == zone &&
          warehouse_data$Day[i] == day) {
        daily_total <- daily_total + warehouse_data$UnitsProcessed[i]
      }
    }

    if (daily_total > highest_total) {
      highest_total <- daily_total
      highest_day <- day
    }
  }

  peak_zone_days$Zone[zone] <- zone
  peak_zone_days$Day[zone] <- highest_day
  peak_zone_days$TotalUnits[zone] <- highest_total
}

peak_zone_days
```

**Explanation:** `peak_zone_days` has one row per zone and identifies the day with the highest total units processed for that zone. If two days tie, the code keeps the first day encountered.

```{r}
## Final validation
```


```{r validation}
cat("Rows:", nrow(warehouse_data), "\\n")
cat("Columns:", ncol(warehouse_data), "\\n")
cat("Missing UnitsProcessed values:",
    sum(is.na(warehouse_data$UnitsProcessed)), "\\n")
cat("Missing ProcessingTime values:",
    sum(is.na(warehouse_data$ProcessingTime)), "\\n")

```

```{r}

# ============================================================
# FINAL VALIDATION
# ============================================================

cat("\nFINAL VALIDATION\n")

# Check the number of rows and columns
cat("Number of rows:", nrow(warehouse_data), "\n")
cat("Number of columns:", ncol(warehouse_data), "\n")

# Check for missing values
cat("Missing UnitsProcessed values:",
    sum(is.na(warehouse_data$UnitsProcessed)), "\n")

cat("Missing ProcessingTime values:",
    sum(is.na(warehouse_data$ProcessingTime)), "\n")

# Check that the required dataset has 840 rows and 5 columns
stopifnot(nrow(warehouse_data) == 840)
stopifnot(ncol(warehouse_data) == 5)

# Check that the important columns contain no missing values
stopifnot(all(!is.na(warehouse_data$UnitsProcessed)))
stopifnot(all(!is.na(warehouse_data$ProcessingTime)))

# Check that ProcessingTime follows the required formula
stopifnot(all(
  abs(warehouse_data$ProcessingTime -
      warehouse_data$UnitsProcessed * 0.85) < 0.000001
))

cat("\nAll validation checks passed successfully.\n")

```

'''


