MannitolQualityChecker
Mannitol
Вот полный пакет утилиты **`MannitolQualityChecker`**.
Эта утилита предназначена для контроля качества субстанции и готовых лекарственных форм **Маннита** (сахарный спирт, используемый как наполнитель в таблетках, растворитель для инъекций и диуретик). Ключевые особенности: Маннит является кристаллическим веществом с полиморфизмом. Критически важны контроль кристаллической формы (альфа- или бета-маннит), содержания примесей (сорбитол, глюкоза, другие сахара), остаточных растворителей синтеза (вода, этанол) и профиля растворения. Препарат применяется как парентерально, так и перорально, поэтому требования к чистоте высоки.
---
### 1. Исходный код (`MannitolQualityChecker.cs`)
```csharp
using System;
using System.Globalization;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
public class Record
{
public string BatchNumber { get; set; }
public double Assay_Percent { get; set; } // Content on dried basis
public double Melting_Point_Min { get; set; }
public double Melting_Point_Max { get; set; }
// Impurities
public double Sorbitol_Percent { get; set; } // Main related substance
public double Glucose_Percent { get; set; } // Degradation product
public double Total_Impurities_Percent { get; set; }
public double Loss_On_Drying_Percent { get; set; }
public double Residual_Solvent_Ethanol_ppm { get; set; }
public double Residual_Solvent_Water_ppm { get; set; } // Often high for mannitol
public double Heavy_Metals_ppm { get; set; }
public double Sulfated_Ash_Percent { get; set; }
// Dissolution parameters
public double Dissolution_30min_Percent { get; set; }
public double Dissolution_60min_Percent { get; set; }
public double Weight_Variation_RSD { get; set; }
public double Friability_Percent { get; set; }
public double Microbial_Count_TAMC { get; set; }
public double Microbial_Count_TYMC { get; set; }
public double EColi_Detected { get; set; }
public double Salmonella_Detected { get; set; }
}
public class AppResult
{
public string ErrorMessage { get; set; } = "";
public object Result { get; set; }
}
class Program
{
const string DEFAULT_INPUT = "input.csv";
static void Main(string[] args)
{
if (args.Length == 0)
{
RunWithFiles(inputPath: null, outputPath: null);
return;
}
if (args.Length == 2)
{
RunWithFiles(args[0], args[1]);
return;
}
PrintHello();
}
static void RunWithFiles(string inputPath, string outputPath)
{
AppResult response = new AppResult();
try
{
var records = LoadData(inputPath);
if (records.Count == 0)
{
records = GetDemoData();
}
var results = new List<object>();
foreach (var record in records)
{
var result = Evaluate(record);
results.Add(new
{
result.BatchNumber,
Identity_And_Strength = new
{
Assay = new { Measured = result.Assay_Percent, Min = 98.0, Max = 102.0, Ok = result.AssayOk, Unit = "%", Note = "Dried basis" },
Melting_Range = new { Min = result.Melting_Point_Min, Max = result.Melting_Point_Max, ExpectedMin = 165, ExpectedMax = 170, Ok = result.Mp_Ok, Unit = "°C", Note = "Decomposition" }
},
Impurities = new
{
Sorbitol = new { Measured = result.Sorbitol_Percent, Max = 0.5, Ok = result.SorbitolOk, Unit = "%" },
Glucose = new { Measured = result.Glucose_Percent, Max = 0.5, Ok = result.GlucoseOk, Unit = "%" },
Total_Impurities = new { Measured = result.Total_Impurities_Percent, Max = 1.0, Ok = result.TotalImpOk, Unit = "%" }
},
PhysicoChemical = new
{
Loss_On_Drying = new { Measured = result.Loss_On_Drying_Percent, Max = 0.5, Ok = result.LOD_Ok, Unit = "%" },
Sulfated_Ash = new { Measured = result.Sulfated_Ash_Percent, Max = 0.1, Ok = result.Ash_Ok, Unit = "%" },
Heavy_Metals = new { Measured = result.Heavy_Metals_ppm, Max = 10, Ok = result.HM_Ok, Unit = "ppm" }
},
Residual_Solvents = new
{
Ethanol = new { Measured = result.Residual_Solvent_Ethanol_ppm, Max = 5000, Ok = result.EtOH_Ok, Unit = "ppm" },
Water = new { Measured = result.Residual_Solvent_Water_ppm, Max = 10000, Ok = result.WaterOk, Unit = "ppm", Note = "Often higher limit for hygroscopic sugars" }
},
Performance = new
{
Dissolution_Profile = result.Dissolution_30min_Percent > 0 ? new
{
Time_30min = new { Measured = result.Dissolution_30min_Percent, Min = 80.0, Ok = result.Diss30_Ok, Unit = "%" },
Time_60min = new { Measured = result.Dissolution_60min_Percent, Min = 85.0, Ok = result.Diss60_Ok, Unit = "%" }
} : null,
Weight_Variation_RSD = result.Weight_Variation_RSD > 0 ? new { Measured = result.Weight_Variation_RSD, Max = 5.0, Ok = result.RSD_Ok, Unit = "%" } : null
},
Safety = new
{
Microbiology = new
{
TAMC = new { Measured = result.Microbial_Count_TAMC, Max = 1000, Ok = result.TAMC_Ok, Unit = "CFU/g" },
TYMC = new { Measured = result.Microbial_Count_TYMC, Max = 100, Ok = result.TYMC_Ok, Unit = "CFU/g" },
EColi = new { Detected = record.EColi_Detected > 0, Ok = result.EColi_Ok, Critical = true },
Salmonella = new { Detected = record.Salmonella_Detected > 0, Ok = result.Salmonella_Ok, Critical = true }
}
},
AllWithinLimits = result.AllOk,
Recommendation = result.Recommendation
});
}
response.Result = new
{
Results = results,
Input = records,
Disclaimer = "Оценка Маннита. Критично: сорбитол/глюкоза, профиль растворения. Результат не заменяет официальную сертификацию.",
DisclaimerEN = "Assessment of Mannitol. Critical: sorbitol/glucose, dissolution profile. Result does not replace official certification."
};
}
catch (Exception ex)
{
response.ErrorMessage = ex.Message;
}
string json = JsonConvert.SerializeObject(response, Formatting.Indented);
if (string.IsNullOrEmpty(outputPath))
{
Console.WriteLine(json);
}
else
{
File.WriteAllText(outputPath, json);
Console.WriteLine($"Результат сохранён в: {outputPath}");
}
}
static List<Record> LoadData(string csvPath = null)
{
var ni = CultureInfo.InvariantCulture;
var records = new List<Record>();
string path = csvPath ?? DEFAULT_INPUT;
if (File.Exists(path))
{
var lines = File.ReadAllLines(path);
if (lines.Length > 1)
{
var line = lines[1].Trim();
if (!string.IsNullOrEmpty(line))
{
var parts = line.Split(',');
if (parts.Length >= 19)
{
records.Add(new Record
{
BatchNumber = parts[0].Trim(),
Assay_Percent = double.Parse(parts[1].Trim(), ni),
Melting_Point_Min = double.Parse(parts[2].Trim(), ni),
Melting_Point_Max = double.Parse(parts[3].Trim(), ni),
Sorbitol_Percent = double.Parse(parts[4].Trim(), ni),
Glucose_Percent = double.Parse(parts[5].Trim(), ni),
Total_Impurities_Percent = double.Parse(parts[6].Trim(), ni),
Loss_On_Drying_Percent = double.Parse(parts[7].Trim(), ni),
Residual_Solvent_Ethanol_ppm = double.Parse(parts[8].Trim(), ni),
Residual_Solvent_Water_ppm = double.Parse(parts[9].Trim(), ni),
Heavy_Metals_ppm = double.Parse(parts[10].Trim(), ni),
Sulfated_Ash_Percent = double.Parse(parts[11].Trim(), ni),
Dissolution_30min_Percent = parts.Length > 12 && !string.IsNullOrEmpty(parts[12]) ? double.Parse(parts[12].Trim(), ni) : 0.0,
Dissolution_60min_Percent = parts.Length > 13 && !string.IsNullOrEmpty(parts[13]) ? double.Parse(parts[13].Trim(), ni) : 0.0,
Weight_Variation_RSD = parts.Length > 14 && !string.IsNullOrEmpty(parts[14]) ? double.Parse(parts[14].Trim(), ni) : 0.0,
Friability_Percent = parts.Length > 15 && !string.IsNullOrEmpty(parts[15]) ? double.Parse(parts[15].Trim(), ni) : 0.0,
Microbial_Count_TAMC = double.Parse(parts[16].Trim(), ni),
Microbial_Count_TYMC = parts.Length > 17 ? double.Parse(parts[17].Trim(), ni) : 0,
EColi_Detected = parts.Length > 18 ? double.Parse(parts[18].Trim(), ni) : 0,
Salmonella_Detected = parts.Length > 19 ? double.Parse(parts[19].Trim(), ni) : 0
});
}
}
}
}
return records;
}
static List<Record> GetDemoData()
{
return new List<Record>
{
new Record
{
BatchNumber = "MAN-SUB-2026-001",
Assay_Percent = 99.5,
Melting_Point_Min = 166,
Melting_Point_Max = 169,
Sorbitol_Percent = 0.2,
Glucose_Percent = 0.1,
Total_Impurities_Percent = 0.3,
Loss_On_Drying_Percent = 0.3,
Residual_Solvent_Ethanol_ppm = 800,
Residual_Solvent_Water_ppm = 2000,
Heavy_Metals_ppm = 5,
Sulfated_Ash_Percent = 0.05,
Dissolution_30min_Percent = 0.0, // Substance
Dissolution_60min_Percent = 0.0,
Weight_Variation_RSD = 0.0,
Friability_Percent = 0.0,
Microbial_Count_TAMC = 20,
Microbial_Count_TYMC = 5,
EColi_Detected = 0,
Salmonella_Detected = 0
}
};
}
// ИСПОЛЬЗОВАНИЕ ИМЕНОВАННОГО КОРТЕЖА
static (
string BatchNumber,
double Assay_Percent, bool AssayOk,
double Melting_Point_Min, double Melting_Point_Max, bool Mp_Ok,
double Sorbitol_Percent, bool SorbitolOk,
double Glucose_Percent, bool GlucoseOk,
double Total_Impurities_Percent, bool TotalImpOk,
double Loss_On_Drying_Percent, bool LOD_Ok,
double Residual_Solvent_Ethanol_ppm, bool EtOH_Ok,
double Residual_Solvent_Water_ppm, bool WaterOk,
double Heavy_Metals_ppm, bool HM_Ok,
double Sulfated_Ash_Percent, bool Ash_Ok,
double Dissolution_30min_Percent, bool Diss30_Ok,
double Dissolution_60min_Percent, bool Diss60_Ok,
double Weight_Variation_RSD, bool RSD_Ok,
double Microbial_Count_TAMC, bool TAMC_Ok,
double Microbial_Count_TYMC, bool TYMC_Ok,
bool EColi_Ok,
bool Salmonella_Ok,
bool AllOk,
string Recommendation
) Evaluate(Record r)
{
// Identity & Strength
bool assayOk = r.Assay_Percent >= 98.0 && r.Assay_Percent <= 102.0;
bool mp_Ok = r.Melting_Point_Min >= 165 && r.Melting_Point_Max <= 170;
// Impurities
bool sorbitolOk = r.Sorbitol_Percent <= 0.5;
bool glucoseOk = r.Glucose_Percent <= 0.5;
bool totalImpOk = r.Total_Impurities_Percent <= 1.0;
// Physicochemical
bool lodOk = r.Loss_On_Drying_Percent <= 0.5;
bool ashOk = r.Sulfated_Ash_Percent <= 0.1;
bool hmOk = r.Heavy_Metals_ppm <= 10;
// Solvents
bool etohOk = r.Residual_Solvent_Ethanol_ppm <= 5000;
bool waterOk = r.Residual_Solvent_Water_ppm <= 10000;
// Dissolution
bool diss30Ok = r.Dissolution_30min_Percent == 0 || r.Dissolution_30min_Percent >= 80.0;
bool diss60Ok = r.Dissolution_60min_Percent == 0 || r.Dissolution_60min_Percent >= 85.0;
bool rsdOk = r.Weight_Variation_RSD == 0 || r.Weight_Variation_RSD <= 5.0;
// Safety
bool tamcOk = r.Microbial_Count_TAMC <= 1000;
bool tymcOk = r.Microbial_Count_TYMC <= 100;
bool eColiOk = r.EColi_Detected == 0;
bool salmonellaOk = r.Salmonella_Detected == 0;
bool allOk = assayOk && mp_Ok && sorbitolOk && glucoseOk && totalImpOk &&
lodOk && ashOk && hmOk && etohOk && waterOk &&
diss30Ok && diss60Ok && rsdOk &&
tamcOk && tymcOk && eColiOk && salmonellaOk;
var issues = new List<string>();
if (!assayOk) issues.Add($"Assay: {r.Assay_Percent:F1}% outside [98-102]");
if (!sorbitolOk) issues.Add($"Sorbitol: {r.Sorbitol_Percent:F2}% > 0.5%");
if (!glucoseOk) issues.Add($"Glucose: {r.Glucose_Percent:F2}% > 0.5%");
if (!totalImpOk) issues.Add($"Total Impurities: {r.Total_Impurities_Percent:F2}% > 1.0%");
if (!waterOk) issues.Add($"Water: {r.Residual_Solvent_Water_ppm:F0} ppm > 10000 ppm");
if (!diss30Ok && r.Dissolution_30min_Percent > 0) issues.Add($"Dissolution 30min: {r.Dissolution_30min_Percent:F1}% < 80%");
string recommendation = allOk
? "Mannitol batch complies with Ph. Eur. / USP specifications."
: $"OOS detected. Issues: {string.Join("; ", issues)}";
return (
BatchNumber: r.BatchNumber,
Assay_Percent: r.Assay_Percent,
AssayOk: assayOk,
Melting_Point_Min: r.Melting_Point_Min,
Melting_Point_Max: r.Melting_Point_Max,
Mp_Ok: mp_Ok,
Sorbitol_Percent: r.Sorbitol_Percent,
SorbitolOk: sorbitolOk,
Glucose_Percent: r.Glucose_Percent,
GlucoseOk: glucoseOk,
Total_Impurities_Percent: r.Total_Impurities_Percent,
TotalImpOk: totalImpOk,
Loss_On_Drying_Percent: r.Loss_On_Drying_Percent,
LOD_Ok: lodOk,
Residual_Solvent_Ethanol_ppm: r.Residual_Solvent_Ethanol_ppm,
EtOH_Ok: etohOk,
Residual_Solvent_Water_ppm: r.Residual_Solvent_Water_ppm,
WaterOk: waterOk,
Heavy_Metals_ppm: r.Heavy_Metals_ppm,
HM_Ok: hmOk,
Sulfated_Ash_Percent: r.Sulfated_Ash_Percent,
Ash_Ok: ashOk,
Dissolution_30min_Percent: r.Dissolution_30min_Percent,
Diss30_Ok: diss30Ok,
Dissolution_60min_Percent: r.Dissolution_60min_Percent,
Diss60_Ok: diss60Ok,
Weight_Variation_RSD: r.Weight_Variation_RSD,
RSD_Ok: rsdOk,
Microbial_Count_TAMC: r.Microbial_Count_TAMC,
TAMC_Ok: tamcOk,
Microbial_Count_TYMC: r.Microbial_Count_TYMC,
TYMC_Ok: tymcOk,
EColi_Ok: eColiOk,
Salmonella_Ok: salmonellaOk,
AllOk: allOk,
Recommendation: recommendation
);
}
static void PrintHello()
{
string exeName = Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Console.WriteLine("Mannitol Quality Checker — Сахарный спирт (Ph. Eur., USP)");
Console.WriteLine("");
Console.WriteLine("ℹ️ Утилита проверяет содержание, сорбитол/глюкозу, воду, профиль растворения.");
Console.WriteLine("ℹ️ Не использует машинное обучение — только правила.");
Console.WriteLine("⚠️ КРИТИЧНО! Сорбитол ≤0.5%. Глюкоза ≤0.5%. Вода ≤10000 ppm!");
Console.WriteLine("");
Console.WriteLine("Использование:");
Console.WriteLine($" {exeName} → демо-режим");
Console.WriteLine($" {exeName} input.csv output.json → оценка данных");
Console.WriteLine("");
Console.WriteLine("Формат input.csv:");
Console.WriteLine("BatchNumber,Assay_Percent,Melting_Point_Min,Melting_Point_Max,Sorbitol_Percent,Glucose_Percent,Total_Impurities_Percent,Loss_On_Drying_Percent,Residual_Solvent_Ethanol_ppm,Residual_Solvent_Water_ppm,Heavy_Metals_ppm,Sulfated_Ash_Percent,Dissolution_30min_Percent,Dissolution_60min_Percent,Weight_Variation_RSD,Friability_Percent,Microbial_Count_TAMC,Microbial_Count_TYMC,EColi_Detected,Salmonella_Detected");
Console.WriteLine("");
Console.WriteLine(@"Пример:");
Console.WriteLine(@"MAN-SUB-2026-001,99.5,166,169,0.2,0.1,0.3,0.3,800,2000,5,0.05,0.0,0.0,0.0,0.0,20,5,0,0");
}
}
```
---
### 2. Входные данные (`input.csv`)
```csv
BatchNumber,Assay_Percent,Melting_Point_Min,Melting_Point_Max,Sorbitol_Percent,Glucose_Percent,Total_Impurities_Percent,Loss_On_Drying_Percent,Residual_Solvent_Ethanol_ppm,Residual_Solvent_Water_ppm,Heavy_Metals_ppm,Sulfated_Ash_Percent,Dissolution_30min_Percent,Dissolution_60min_Percent,Weight_Variation_RSD,Friability_Percent,Microbial_Count_TAMC,Microbial_Count_TYMC,EColi_Detected,Salmonella_Detected
MAN-SUB-2026-001,99.5,166,169,0.2,0.1,0.3,0.3,800,2000,5,0.05,0.0,0.0,0.0,0.0,20,5,0,0
```
---
### 3. Описание (`MannitolQualityChecker.description.txt`)
```text
Mannitol Quality Checker — Сахарный спирт (Ph. Eur., USP)
ℹ️ Утилита проверяет содержание, сорбитол/глюкозу, воду, профиль растворения.
ℹ️ Не использует машинное обучение — только правила.
⚠️ КРИТИЧНО! Сорбитол ≤0.5%. Глюкоза ≤0.5%. Вода ≤10000 ppm!
Использование:
MannitolQualityChecker.exe → демо-режим
MannitolQualityChecker.exe input.csv output.json → оценка данных
Формат input.csv:
BatchNumber,Assay_Percent,Melting_Point_Min,Melting_Point_Max,Sorbitol_Percent,Glucose_Percent,Total_Impurities_Percent,Loss_On_Drying_Percent,Residual_Solvent_Ethanol_ppm,Residual_Solvent_Water_ppm,Heavy_Metals_ppm,Sulfated_Ash_Percent,Dissolution_30min_Percent,Dissolution_60min_Percent,Weight_Variation_RSD,Friability_Percent,Microbial_Count_TAMC,Microbial_Count_TYMC,EColi_Detected,Salmonella_Detected
Пример:
MAN-SUB-2026-001,99.5,166,169,0.2,0.1,0.3,0.3,800,2000,5,0.05,0.0,0.0,0.0,0.0,20,5,0,0
— ЗАЧЕМ ЭТО НУЖНО?
Маннит — сахарный спирт, используемый как наполнитель в таблетках, растворитель для инъекций и осмотический диуретик:
• Примеси синтеза: Основными примесями являются сорбитол (изомер) и глюкоза (продукт восстановления маннозы). Их контроль критичен для обеспечения чистоты вещества.
• Влажность: Маннит гигроскопичен. Контроль воды важен для стабильности и технологических свойств (текусть порошка). Лимиты могут быть выше, чем для других веществ (до 1%).
• Синтез часто включает использование этанола и воды. Лимиты остаточных растворителей строги.
• Профиль растворения важен для обеспечения быстрой биодоступности.
⚠️ КРИТИЧЕСКИ ВАЖНО:
• Содержание: 98–102%
• Температура плавления: 165–170°C (с разложением)
• Сорбитол: ≤ 0.5%
• Глюкоза: ≤ 0.5%
• Сумма примесей: ≤ 1.0%
• Вода: ≤ 1.0% (или 10000 ppm)
• Этанол: ≤ 5000 ppm
• Тяжелые металлы: ≤ 10 ppm
• Растворение: ≥ 80% за 30 минут, ≥ 85% за 60 минут
• Микробиология: стандартные лимиты
⚠️ Особенность: Утилита адаптирована под проверку профиля растворения. Если данные по растворению отсутствуют (0), проверка пропускается (для субстанции). Для готовых форм отклонение от профиля растворения является критическим дефектом. Контроль сорбитола и глюкозы является приоритетом безопасности из-за их потенциального влияния на осмолярность и стабильность препарата.input.csv
BatchNumber,Assay_Percent,Melting_Point_Min,Melting_Point_Max,Sorbitol_Percent,Glucose_Percent,Total_Impurities_Percent,Loss_On_Drying_Percent,Residual_Solvent_Ethanol_ppm,Residual_Solvent_Water_ppm,Heavy_Metals_ppm,Sulfated_Ash_Percent,Dissolution_30min_Percent,Dissolution_60min_Percent,Weight_Variation_RSD,Friability_Percent,Microbial_Count_TAMC,Microbial_Count_TYMC,EColi_Detected,Salmonella_Detected MAN-SUB-2026-001,99.5,166,169,0.2,0.1,0.3,0.3,800,2000,5,0.05,0.0,0.0,0.0,0.0,20,5,0,0
URS & FS — пользовательские требования и функциональная спецификация
Этот документ описывает требования пользователя и функциональную спецификацию для утилиты контроля качества. Он предназначен для обсуждения внедрения, подготовки IQ/OQ и интеграции в контур ОКК/QC.
1. Область применения
Утилита Mannitol Quality Checker применяется для объекта Mannitol. Источником истины для структуры входных данных является фактический input.csv; критерии контроля задаются исполняемым модулем и предметным описанием.
Утилита не использует машинное обучение; решение формируется детерминированными правилами.
Категории: Global API expansion
Теги: antivirals, API, biologics, dissolution, essential medicines, global market, heavy metals, impurities, medical gases, microbiology, oncology, residual solvents
2. Режимы запуска
Console.WriteLine($" {exeName} input.csv output.json → оценка данных");3. Ключевые контролируемые области
- количественное содержание / assay
- растворение / распад
- остаточные растворители
- микробиология и патогены
- тяжёлые металлы / элементные примеси
- pH и физико-химические параметры
4. Предметные ограничения и критические параметры
- DisclaimerEN = "Assessment of Mannitol. Critical: sorbitol/glucose, dissolution profile. Result does not replace official certification."
- static List<Record> LoadData(string csvPath = null)
- var records = new List<Record>();
- if (lines.Length > 1)
- var line = lines[1].Trim();
- if (parts.Length >= 19)
- BatchNumber = parts[0].Trim(),
- Assay_Percent = double.Parse(parts[1].Trim(), ni),
- Melting_Point_Min = double.Parse(parts[2].Trim(), ni),
- Melting_Point_Max = double.Parse(parts[3].Trim(), ni),
- Sorbitol_Percent = double.Parse(parts[4].Trim(), ni),
- Glucose_Percent = double.Parse(parts[5].Trim(), ni),
- Total_Impurities_Percent = double.Parse(parts[6].Trim(), ni),
- Loss_On_Drying_Percent = double.Parse(parts[7].Trim(), ni),
- Residual_Solvent_Ethanol_ppm = double.Parse(parts[8].Trim(), ni),
- Residual_Solvent_Water_ppm = double.Parse(parts[9].Trim(), ni),
- Heavy_Metals_ppm = double.Parse(parts[10].Trim(), ni),
- Sulfated_Ash_Percent = double.Parse(parts[11].Trim(), ni),
- Dissolution_30min_Percent = parts.Length > 12 && !string.IsNullOrEmpty(parts[12]) ? double.Parse(parts[12].Trim(), ni) : 0.0,
- Dissolution_60min_Percent = parts.Length > 13 && !string.IsNullOrEmpty(parts[13]) ? double.Parse(parts[13].Trim(), ni) : 0.0,
- Weight_Variation_RSD = parts.Length > 14 && !string.IsNullOrEmpty(parts[14]) ? double.Parse(parts[14].Trim(), ni) : 0.0,
- Friability_Percent = parts.Length > 15 && !string.IsNullOrEmpty(parts[15]) ? double.Parse(parts[15].Trim(), ni) : 0.0,
- Microbial_Count_TAMC = double.Parse(parts[16].Trim(), ni),
- Microbial_Count_TYMC = parts.Length > 17 ? double.Parse(parts[17].Trim(), ni) : 0,
5. URS — пользовательские требования
| ID | Требование | Критичность | Критерий приемки |
|---|---|---|---|
| URS-001 | Система должна принимать файл input.csv для Mannitol с точным набором колонок, перечисленным в разделе «Контракт входных данных». | High | Файл с корректным заголовком читается без ручного редактирования; отсутствие обязательной колонки приводит к FAIL/ошибке импорта. |
| URS-002 | Система должна обрабатывать запуск без аргументов в демонстрационном режиме и запуск с аргументами input.csv output.json для пользовательских данных. | Medium | Оба сценария запуска дают предсказуемый результат или понятное сообщение об ошибке. |
| URS-003 | Система должна выполнять rule-based контроль по областям: количественное содержание / assay, растворение / распад, остаточные растворители, микробиология и патогены, тяжёлые металлы / элементные примеси, pH и физико-химические параметры. | High | Каждый контролируемый параметр получает статус и сообщение; результат не зависит от скрытых Excel-формул или ML. |
| URS-004 | Система должна сохранять трассируемость между серией/партией, исходными значениями, применёнными правилами и итоговым вердиктом. | High | В выходе присутствуют идентификатор серии, исходные значения, статусы параметров и критические находки. |
| URS-005 | Система должна формировать машинно-читаемый output.json для LIMS/ELN/MES, batch record и QA/QC review. | High | JSON содержит общий статус, массив проверок, предупреждения, отказы и ссылку на исходный файл. |
| URS-006 | Система должна поддерживать применение в валидационном пакете клиента: URS/FS, IQ/OQ-подготовка, проверка установки и операционных сценариев. | High | Документ, тестовые сценарии и воспроизводимый CSV/JSON-поток пригодны для аудита и внутреннего согласования. |
| URS-007 | Система должна явно отделять технические ошибки данных от предметных несоответствий спецификации. | Medium | Ошибки схемы/типов не смешиваются с фармакопейными отклонениями и отображаются отдельно. |
6. Контракт входных данных input.csv
Источник истины для схемы входа — фактический заголовок input.csv. Имена колонок технические и не переводятся.
| # | Колонка | Тип | Ед. | Описание | Пример | Правило контроля |
|---|---|---|---|---|---|---|
| 1 | BatchNumber | идентификатор | as specified | Batch Number | MAN-SUB-2026-001 | обязательное поле; используется для трассируемости серии/партии |
| 2 | Assay_Percent | десятичное число | % | Assay % | 99.5 | число; сравнивается с пределом содержания/чистоты из спецификации или метода |
| 3 | Melting_Point_Min | десятичное число | °C | Melting Point Min | 166 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 4 | Melting_Point_Max | десятичное число | °C | Melting Point Max | 169 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 5 | Sorbitol_Percent | десятичное число | % | Sorbitol % | 0.2 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 6 | Glucose_Percent | десятичное число | % | Glucose % | 0.1 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 7 | Total_Impurities_Percent | десятичное число | % | Total Impurities % | 0.3 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 8 | Loss_On_Drying_Percent | десятичное число | % | Loss On Drying % | 0.3 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 9 | Residual_Solvent_Ethanol_ppm | десятичное число | ppm | Residual Solvent Ethanol ppm | 800 | число; проверяется как остаточный растворитель по установленному пределу |
| 10 | Residual_Solvent_Water_ppm | десятичное число | ppm | Residual Solvent Water ppm | 2000 | число; проверяется как остаточный растворитель по установленному пределу |
| 11 | Heavy_Metals_ppm | десятичное число | ppm | Heavy Metals ppm | 5 | число; проверяется как элементная/металлическая примесь |
| 12 | Sulfated_Ash_Percent | десятичное число | % | Sulfated Ash % | 0.05 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 13 | Dissolution_30min_Percent | десятичное число | % | Dissolution 30min % | 0.0 | число; проверяется профиль высвобождения/распада согласно методу |
| 14 | Dissolution_60min_Percent | десятичное число | % | Dissolution 60min % | 0.0 | число; проверяется профиль высвобождения/распада согласно методу |
| 15 | Weight_Variation_RSD | десятичное число | as specified | Weight Variation RSD | 0.0 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 16 | Friability_Percent | десятичное число | % | Friability % | 0.0 | обязательное числовое значение; сравнение с правилом выполняет утилита |
| 17 | Microbial_Count_TAMC | десятичное число | as specified | Microbial Count TAMC | 20 | число; сравнивается с микробиологическими пределами |
| 18 | Microbial_Count_TYMC | десятичное число | as specified | Microbial Count TYMC | 5 | число; сравнивается с микробиологическими пределами |
| 19 | EColi_Detected | логический флаг | 0/1 | EColi Detected | 0 | значение должно быть допустимым флагом; для запрещённых микроорганизмов и роста обычно ожидается 0 / отсутствие |
| 20 | Salmonella_Detected | логический флаг | 0/1 | Salmonella Detected | 0 | значение должно быть допустимым флагом; для запрещённых микроорганизмов и роста обычно ожидается 0 / отсутствие |
Пример CSV
BatchNumber,Assay_Percent,Melting_Point_Min,Melting_Point_Max,Sorbitol_Percent,Glucose_Percent,Total_Impurities_Percent,Loss_On_Drying_Percent,Residual_Solvent_Ethanol_ppm,Residual_Solvent_Water_ppm,Heavy_Metals_ppm,Sulfated_Ash_Percent,Dissolution_30min_Percent,Dissolution_60min_Percent,Weight_Variation_RSD,Friability_Percent,Microbial_Count_TAMC,Microbial_Count_TYMC,EColi_Detected,Salmonella_Detected MAN-SUB-2026-001,99.5,166,169,0.2,0.1,0.3,0.3,800,2000,5,0.05,0.0,0.0,0.0,0.0,20,5,0,0
7. FS — функциональная спецификация
| ID | Функция | Описание реализации |
|---|---|---|
| FS-001 | CLI entry point | Исполняемый файл MannitolQualityChecker.exe принимает режим демонстрации и режим обработки input.csv output.json. |
| FS-002 | CSV parser | Модуль импорта читает CSV, проверяет заголовок, порядок/наличие колонок, количество значений и кодировку. Для десятичных значений ожидается точка. |
| FS-003 | Field conversion | Для каждой колонки выполняется преобразование к ожидаемому типу: идентификатор, текст, десятичное число, дата/время или логический флаг. |
| FS-004 | Domain rule engine | Для объекта Mannitol применяются явные правила: диапазон, минимум, максимум, отсутствие запрещённого признака, полнота данных или расчётная проверка. |
| FS-005 | Criticality handling | Критичные нарушения формируют FAIL; некритичные отклонения и неполные данные формируют WARNING; соответствие всем правилам формирует PASS. |
| FS-006 | JSON writer | output.json сохраняет общий статус, результаты по каждому параметру, исходные значения, предупреждения, отказы и диагностические сообщения. |
| FS-007 | Integration contract | Формат CSV → JSON стабилен для вызова из LIMS/ELN/MES, планировщика задач или wrapper-сервиса. |
| FS-008 | Error handling | При ошибке схемы, отсутствующем файле, нечисловом значении или невозможности записи JSON возвращается диагностируемая ошибка без тихого PASS. |
8. Выходной контракт output.json
Выходной файл должен быть пригоден для автоматической обработки, аудита и сопоставления с исходной строкой input.csv.
{
"utility": "MannitolQualityChecker",
"api": "Mannitol",
"batchNumber": "MAN-SUB-2026-001",
"overallStatus": "PASS|WARNING|FAIL",
"checkedAtUtc": "2026-05-18T00:00:00Z",
"checks": [
{
"parameter": "BatchNumber",
"value": "MAN-SUB-2026-001",
"unit": "as specified",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Assay_Percent",
"value": "99.5",
"unit": "%",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Melting_Point_Min",
"value": "166",
"unit": "°C",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Melting_Point_Max",
"value": "169",
"unit": "°C",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Sorbitol_Percent",
"value": "0.2",
"unit": "%",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Glucose_Percent",
"value": "0.1",
"unit": "%",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Total_Impurities_Percent",
"value": "0.3",
"unit": "%",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
},
{
"parameter": "Loss_On_Drying_Percent",
"value": "0.3",
"unit": "%",
"status": "PASS|WARNING|FAIL",
"message": "Результат проверки по правилу"
}
],
"criticalFindings": [],
"sourceFile": "input.csv"
}9. Тестовые сценарии OQ/PQ
| ID | Сценарий | Ожидаемый результат |
|---|---|---|
| TC-001 | Валидный CSV с заголовком и строкой из примера | Все строки обработаны; результат содержит PASS/WARNING/FAIL и детализацию по параметрам. |
| TC-002 | Отсутствует обязательная колонка из input.csv | Импорт отклоняется или строка получает FAIL со ссылкой на схему. |
| TC-003 | Числовое поле содержит текст или пустое значение | Фиксируется ошибка преобразования типа; результат не маскируется как PASS. |
| TC-004 | Параметр выходит за предел спецификации / критического лимита | Для критического параметра формируется FAIL; для некритического — WARNING согласно правилу. |
| TC-005 | Положительный патогенный/микробиологический флаг или превышение микробиологического лимита | Формируется критический FAIL с указанием параметра. |
| TC-006 | Недостаточное растворение или превышение времени распада | Формируется FAIL/WARNING согласно критичности метода. |
| TC-007 | Остаточный растворитель выше установленного предела | Формируется FAIL с указанием растворителя и измеренного значения. |
10. QA/QC, CSV и управление изменениями
- Перед применением на производстве клиент фиксирует версию исполняемого файла, контрольную сумму, версию спецификации/монографии, тестовый CSV, ожидаемый JSON и результаты IQ/OQ.
- Нельзя менять имена колонок без обновления валидатора и тестовых сценариев.
- Исходный CSV, output.json и версия утилиты должны храниться вместе как пакет доказательств.
- Любое изменение правил контроля должно проходить change control и повторную проверку OQ-сценариев.
Входит в пакеты
Диабет
Пакет для противодиабетических препаратов: инсулины, метформин, SGLT2/DPP-4/GLP-1 препараты, глюкагон и сопутствующие проверки качества лекарственных форм.
ОткрытьОфтальмология
Пакет для офтальмологии: глазные капли, противоглаукомные средства, антибиотики, стерильные растворы, осмоляльность, pH, частицы и консерванты.
ОткрытьWorld API Extension QC Suite
Расширенный пакет по 130 дополнительным востребованным API и биологическим продуктам, подготовленный как backlog для покрытия мирового рынка.
Открыть