cyclia
Warning: This library is not a medical device and does not replace professional medical advice. All predictions are probabilistic and for informational purposes only.
Library for predicting menstrual cycles based on historical data. Provides accurate algorithms for forecasting upcoming periods, ovulation, and fertile windows.
🚀 Features
- Multiple prediction strategies: Calendar and WMA (Weighted Moving Average)
- Data quality analysis: automatic assessment of cycle regularity
- Ovulation prediction: based on luteal phase
- Fertile window detection: for pregnancy planning
- Pregnancy tracking: current week, trimester, and milestones
- Confidence system: confidence score for each prediction
- Extensible architecture: ability to add custom algorithms
- TypeScript support: full typing out of the box
📦 Installation
npm install cyclia
yarn add cyclia
pnpm add cyclia
📖 Documentation
Detailed usage examples and API are described on the official website.
🎯 Quick Start
Basic Usage
import { PredictionEngine } from "cyclia";
const engine = new PredictionEngine({ strategy: "wma" });
const history = {
periodStarts: [
{ date: "2025-01-02" },
{ date: "2025-01-31" }, // 29 дней
{ date: "2025-03-01" }, // 29 дней
{ date: "2025-03-29" }, // 28 дней
{ date: "2025-04-27" }, // 29 дней
],
};
// Predict next period
const nextPeriod = engine.predictNextPeriod(history);
console.log("Next period:", nextPeriod.likely);
console.log("Confidence:", Math.round(nextPeriod.confidence * 100) + "%");
// Predict ovulation
const ovulation = engine.predictOvulation(history);
console.log("Ovulation:", ovulation.likely);
// Fertile window
const fertile = engine.predictFertileWindow(history);
console.log("Fertile window:", fertile.start, "-", fertile.end);
// Pregnancy prediction
const pregnancy = engine.predictPregnancy(new Date("2025-01-15"));
console.log("Pregnancy week:", pregnancy.currentWeek);
console.log("Due date:", pregnancy.dueDate);
Data Analysis
// Get analytics summary
const summary = engine.analyze(history);
console.log("Average cycle length:", summary.averageCycle);
console.log("Regularity:", summary.irregular ? "Irregular" : "Regular");
console.log("Data quality:", summary.dataQuality);
📚 API Reference
PredictionEngine
Main class for working with predictions.
Constructor
new PredictionEngine(config?: PredictorConfig)
Configuration (PredictorConfig)
Parameter | Type | Default | Description | |
---|---|---|---|---|
strategy |
`'wma' \ | 'calendar'` | 'wma' |
Prediction strategy |
lutealPhaseDays |
number |
14 |
Luteal phase duration | |
irregularityStdThreshold |
number |
4 |
Irregularity threshold (standard deviation) | |
minIntervalsForConfidence |
number |
3 |
Minimum intervals for confidence | |
timezone |
string |
Local TZ | Timezone for calculations |
Methods
analyze(history: HistoryInput): AnalyticsSummary
Analyzes historical data and returns a summary.
const summary = engine.analyze(history);
// {
// sampleSize: 4,
// cycleIntervals: [29, 29, 28, 29],
// averageCycle: 28.75,
// medianCycle: 29,
// stdCycle: 0.5,
// irregular: false,
// dataQuality: "medium"
// }
predictNextPeriod(history: HistoryInput): PredictionResult
Predicts the date of the next period.
const result = engine.predictNextPeriod(history);
// {
// likely: "2025-05-26",
// window: { start: "2025-05-24", end: "2025-05-28" },
// confidence: 0.8,
// notes: ["wma: weighted last intervals", "irregularity: ±2d"]
// }
predictOvulation(history: HistoryInput): PredictionResult
Predicts the date of ovulation.
predictFertileWindow(history: HistoryInput): FertileWindow
Determines the fertile window.
const fertile = engine.predictFertileWindow(history);
// {
// start: "2025-05-07",
// peak: "2025-05-12",
// end: "2025-05-13",
// confidence: 0.7,
// notes: ["ovulation = nextPeriod - lutealPhaseDays", "fertile = ovulation ± (−5..+1)"]
// }
predictPregnancy(lastPeriodDate: Date): PregnancyPrediction
Predicts pregnancy progress based on last period date.
const pregnancy = engine.predictPregnancy(new Date("2025-01-15"));
// {
// dueDate: Date,
// currentWeek: 12,
// currentTrimester: 1,
// daysRemaining: 196,
// milestones: ["Завершение первого триместра", "Снижение риска выкидыша"]
// }
predictPregnancyFromHistory(history: HistoryInput): PregnancyPrediction
Predicts pregnancy progress using the last period date from cycle history.
const pregnancy = engine.predictPregnancyFromHistory(history);
// Uses the most recent period date from history
Data Types
HistoryInput
interface HistoryInput {
periodStarts: PeriodStartRecord[];
}
interface PeriodStartRecord {
date: ISODate; // YYYY-MM-DD
}
PredictionResult
interface PredictionResult {
likely: ISODate | null; // Вероятная дата
window: DateRange | null; // Окно прогноза
confidence: number; // Уверенность (0-1)
notes: string[]; // Комментарии
}
FertileWindow
interface FertileWindow {
start: ISODate; // Начало фертильного окна
peak: ISODate; // Пик фертильности (овуляция)
end: ISODate; // Конец фертильного окна
confidence: number; // Уверенность
notes: string[]; // Комментарии
}
PregnancyPrediction
interface PregnancyPrediction {
dueDate: Date; // Предполагаемая дата родов
currentWeek: number; // Текущая неделя беременности
currentTrimester: number; // Текущий триместр (1-3)
daysRemaining: number; // Дней до родов
milestones: string[]; // Вехи развития для текущей недели
}
🎨 Integration with UI Frameworks
React/Next.js
Prediction Hook
import { useState, useEffect } from "react";
import { PredictionEngine, HistoryInput } from "cyclia";
const useCyclePredictions = (history: HistoryInput) => {
const [predictions, setPredictions] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (history.periodStarts.length >= 2) {
setLoading(true);
setError(null);
try {
const engine = new PredictionEngine({ strategy: "wma" });
const nextPeriod = engine.predictNextPeriod(history);
const ovulation = engine.predictOvulation(history);
const fertile = engine.predictFertileWindow(history);
const summary = engine.analyze(history);
setPredictions({ nextPeriod, ovulation, fertile, summary });
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
}, [history]);
return { predictions, loading, error };
};
Calendar Component
import React from 'react';
import { useCyclePredictions } from './hooks/useCyclePredictions';
interface CycleCalendarProps {
periodHistory: HistoryInput;
}
const CycleCalendar: React.FC<CycleCalendarProps> = ({ periodHistory }) => {
const { predictions, loading, error } = useCyclePredictions(periodHistory);
if (loading) return <div>Анализируем данные...</div>;
if (error) return <div>Ошибка: {error}</div>;
if (!predictions) return <div>Недостаточно данных для прогноза</div>;
return (
<div className="cycle-calendar">
<div className="prediction-card">
<h3>Следующая менструация</h3>
<div className="date">{predictions.nextPeriod.likely}</div>
<div className="confidence">
Уверенность: {Math.round(predictions.nextPeriod.confidence * 100)}%
</div>
{predictions.nextPeriod.window && (
<div className="window">
Окно: {predictions.nextPeriod.window.start} - {predictions.nextPeriod.window.end}
</div>
)}
</div>
<div className="prediction-card">
<h3>Овуляция</h3>
<div className="date">{predictions.ovulation.likely}</div>
<div className="confidence">
Уверенность: {Math.round(predictions.ovulation.confidence * 100)}%
</div>
</div>
<div className="prediction-card">
<h3>Фертильное окно</h3>
<div className="fertile-range">
{predictions.fertile.start} - {predictions.fertile.end}
</div>
<div className="peak">Пик: {predictions.fertile.peak}</div>
</div>
<div className="analytics">
<h3>Анализ данных</h3>
<p>Средний цикл: {predictions.summary.averageCycle} дней</p>
<p>Регулярность: {predictions.summary.irregular ? 'Нерегулярный' : 'Регулярный'}</p>
<p>Качество данных: {predictions.summary.dataQuality}</p>
</div>
</div>
);
};
Vue.js
Composition API
<template>
<div class="cycle-predictor">
<div v-if="loading">Анализируем данные...</div>
<div v-else-if="error">Ошибка: {{ error }}</div>
<div v-else-if="predictions" class="predictions">
<div class="prediction-card">
<h3>Следующая менструация</h3>
<p>{{ predictions.nextPeriod.likely }}</p>
<p>Уверенность: {{ Math.round(predictions.nextPeriod.confidence * 100) }}%</p>
</div>
<div class="prediction-card">
<h3>Овуляция</h3>
<p>{{ predictions.ovulation.likely }}</p>
</div>
<div class="prediction-card">
<h3>Фертильное окно</h3>
<p>{{ predictions.fertile.start }} - {{ predictions.fertile.end }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { PredictionEngine, type HistoryInput } from 'cyclia';
const props = defineProps<{
periodHistory: HistoryInput;
}>();
const predictions = ref(null);
const loading = ref(false);
const error = ref(null);
const calculatePredictions = async () => {
if (props.periodHistory.periodStarts.length < 2) {
predictions.value = null;
return;
}
loading.value = true;
error.value = null;
try {
const engine = new PredictionEngine({ strategy: 'wma' });
predictions.value = {
nextPeriod: engine.predictNextPeriod(props.periodHistory),
ovulation: engine.predictOvulation(props.periodHistory),
fertile: engine.predictFertileWindow(props.periodHistory),
summary: engine.analyze(props.periodHistory)
};
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
watch(() => props.periodHistory, calculatePredictions, { immediate: true });
</script>
Angular
Service
import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable } from "rxjs";
import {
PredictionEngine,
type HistoryInput,
type PredictionResult,
} from "cyclia";
export interface CyclePredictions {
nextPeriod: PredictionResult;
ovulation: PredictionResult;
fertile: any;
summary: any;
}
@Injectable({
providedIn: "root",
})
export class CyclePredictionService {
private engine = new PredictionEngine({ strategy: "wma" });
private predictionsSubject = new BehaviorSubject<CyclePredictions | null>(
null
);
private loadingSubject = new BehaviorSubject<boolean>(false);
predictions$ = this.predictionsSubject.asObservable();
loading$ = this.loadingSubject.asObservable();
calculatePredictions(history: HistoryInput): void {
if (history.periodStarts.length < 2) {
this.predictionsSubject.next(null);
return;
}
this.loadingSubject.next(true);
try {
const predictions = {
nextPeriod: this.engine.predictNextPeriod(history),
ovulation: this.engine.predictOvulation(history),
fertile: this.engine.predictFertileWindow(history),
summary: this.engine.analyze(history),
};
this.predictionsSubject.next(predictions);
} catch (error) {
console.error("Ошибка расчета прогнозов:", error);
} finally {
this.loadingSubject.next(false);
}
}
}
Component
import { Component, Input, OnInit } from "@angular/core";
import {
CyclePredictionService,
type CyclePredictions,
} from "./cycle-prediction.service";
import { type HistoryInput } from "cyclia";
@Component({
selector: "app-cycle-predictor",
template: `
<div class="cycle-predictor">
<div *ngIf="loading$ | async" class="loading">Анализируем данные...</div>
<div *ngIf="predictions$ | async as predictions" class="predictions">
<div class="prediction-card">
<h3>Следующая менструация</h3>
<p class="date">{{ predictions.nextPeriod.likely }}</p>
<p class="confidence">
Уверенность:
{{ predictions.nextPeriod.confidence * 100 | number: "1.0-0" }}%
</p>
</div>
<div class="prediction-card">
<h3>Овуляция</h3>
<p class="date">{{ predictions.ovulation.likely }}</p>
</div>
<div class="prediction-card">
<h3>Фертильное окно</h3>
<p class="range">
{{ predictions.fertile.start }} - {{ predictions.fertile.end }}
</p>
<p class="peak">Пик: {{ predictions.fertile.peak }}</p>
</div>
</div>
</div>
`,
styleUrls: ["./cycle-predictor.component.scss"],
})
export class CyclePredictorComponent implements OnInit {
@Input() periodHistory!: HistoryInput;
predictions$ = this.predictionService.predictions$;
loading$ = this.predictionService.loading$;
constructor(private predictionService: CyclePredictionService) {}
ngOnInit() {
this.predictionService.calculatePredictions(this.periodHistory);
}
}
📱 Mobile Applications
React Native
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { PredictionEngine, type HistoryInput } from 'cyclia';
interface CyclePredictorProps {
periodHistory: HistoryInput;
}
const CyclePredictor: React.FC<CyclePredictorProps> = ({ periodHistory }) => {
const [predictions, setPredictions] = React.useState(null);
const [loading, setLoading] = React.useState(false);
React.useEffect(() => {
if (periodHistory.periodStarts.length >= 2) {
setLoading(true);
const engine = new PredictionEngine({ strategy: 'wma' });
const nextPeriod = engine.predictNextPeriod(periodHistory);
const ovulation = engine.predictOvulation(periodHistory);
const fertile = engine.predictFertileWindow(periodHistory);
setPredictions({ nextPeriod, ovulation, fertile });
setLoading(false);
}
}, [periodHistory]);
if (loading) {
return (
<View style={styles.container}>
<Text>Анализируем данные...</Text>
</View>
);
}
if (!predictions) {
return (
<View style={styles.container}>
<Text>Недостаточно данных для прогноза</Text>
</View>
);
}
return (
<View style={styles.container}>
<View style={styles.card}>
<Text style={styles.title}>Следующая менструация</Text>
<Text style={styles.date}>{predictions.nextPeriod.likely}</Text>
<Text style={styles.confidence}>
Уверенность: {Math.round(predictions.nextPeriod.confidence * 100)}%
</Text>
</View>
<View style={styles.card}>
<Text style={styles.title}>Овуляция</Text>
<Text style={styles.date}>{predictions.ovulation.likely}</Text>
</View>
<View style={styles.card}>
<Text style={styles.title}>Фертильное окно</Text>
<Text style={styles.range}>
{predictions.fertile.start} - {predictions.fertile.end}
</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
},
card: {
backgroundColor: '#fff',
padding: 16,
marginBottom: 12,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
date: {
fontSize: 16,
color: '#333',
},
confidence: {
fontSize: 14,
color: '#666',
marginTop: 4,
},
range: {
fontSize: 16,
color: '#333',
},
});
export default CyclePredictor;
🔧 Extending Functionality
Creating Custom Algorithms
import {
BaseRule,
type HistoryInput,
type PredictionResult,
type PredictorConfig,
type AnalyticsSummary,
} from "cyclia";
class MLPredictionRule extends BaseRule {
readonly id = "ml-prediction";
predictNextPeriod(
history: HistoryInput,
cfg: Required<PredictorConfig>,
summary: AnalyticsSummary
): PredictionResult {
// Ваша ML логика здесь
const mlPrediction = this.runMLModel(history, summary);
return {
likely: mlPrediction.date,
window: {
start: mlPrediction.startDate,
end: mlPrediction.endDate,
},
confidence: mlPrediction.confidence,
notes: ["ML-based prediction", `model: ${mlPrediction.modelName}`],
};
}
private runMLModel(history: HistoryInput, summary: AnalyticsSummary) {
// Интеграция с вашей ML моделью
// Например, TensorFlow.js, ONNX.js, или внешний API
return {
date: "2025-06-15",
startDate: "2025-06-13",
endDate: "2025-06-17",
confidence: 0.85,
modelName: "cycle-predictor-v1",
};
}
}
// Register in engine
const engine = new PredictionEngine();
engine.register(new MLPredictionRule());
// Usage
const result = engine.predictNextPeriod(history);
Integration with External Data
class EnhancedPredictionEngine extends PredictionEngine {
async predictWithExternalFactors(history: HistoryInput) {
const basePrediction = this.predictNextPeriod(history);
// Получаем дополнительные факторы
const weatherData = await this.getWeatherData();
const stressLevel = await this.getStressLevel();
const sleepQuality = await this.getSleepQuality();
// Корректируем прогноз
return this.adjustPrediction(basePrediction, {
weather: weatherData,
stress: stressLevel,
sleep: sleepQuality,
});
}
private adjustPrediction(basePrediction: PredictionResult, factors: any) {
let adjustedConfidence = basePrediction.confidence;
const notes = [...basePrediction.notes];
// Корректировка на основе факторов
if (factors.stress > 7) {
adjustedConfidence *= 0.9;
notes.push("high stress detected");
}
if (factors.sleep < 6) {
adjustedConfidence *= 0.95;
notes.push("poor sleep quality");
}
return {
...basePrediction,
confidence: Math.max(0.1, adjustedConfidence),
notes,
};
}
}
🗄️ Working with Data
Saving and Loading
class CycleDataService {
private readonly STORAGE_KEY = "cycle_history";
private readonly PREDICTIONS_KEY = "cycle_predictions";
async savePeriodDate(date: string): Promise<void> {
const history = await this.loadHistory();
history.periodStarts.push({ date });
await this.saveHistory(history);
// Пересчитываем прогнозы
const engine = new PredictionEngine();
const predictions = engine.predictNextPeriod(history);
await this.savePredictions(predictions);
}
async loadHistory(): Promise<HistoryInput> {
const stored = localStorage.getItem(this.STORAGE_KEY);
return stored ? JSON.parse(stored) : { periodStarts: [] };
}
async saveHistory(history: HistoryInput): Promise<void> {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(history));
}
async getCurrentPredictions(): Promise<any> {
const history = await this.loadHistory();
const engine = new PredictionEngine();
return {
nextPeriod: engine.predictNextPeriod(history),
ovulation: engine.predictOvulation(history),
fertile: engine.predictFertileWindow(history),
summary: engine.analyze(history),
};
}
}
Data Export
class DataExportService {
exportToCSV(history: HistoryInput): string {
const headers = ["Date", "Cycle Length"];
const rows = [];
for (let i = 1; i < history.periodStarts.length; i++) {
const prev = new Date(history.periodStarts[i - 1].date);
const curr = new Date(history.periodStarts[i].date);
const cycleLength = Math.round(
(curr.getTime() - prev.getTime()) / (1000 * 60 * 60 * 24)
);
rows.push([history.periodStarts[i].date, cycleLength]);
}
return [headers, ...rows].map((row) => row.join(",")).join("\n");
}
exportToJSON(history: HistoryInput, predictions: any): string {
return JSON.stringify(
{
history,
predictions,
exportDate: new Date().toISOString(),
},
null,
2
);
}
}
🧪 Testing
Unit Tests
import { PredictionEngine } from "cycle-predictor-core";
describe("PredictionEngine", () => {
let engine: PredictionEngine;
beforeEach(() => {
engine = new PredictionEngine({ strategy: "wma" });
});
it("should predict next period correctly", () => {
const history = {
periodStarts: [
{ date: "2025-01-01" },
{ date: "2025-01-30" }, // 29 дней
{ date: "2025-02-28" }, // 29 дней
],
};
const result = engine.predictNextPeriod(history);
expect(result.likely).toBeTruthy();
expect(result.confidence).toBeGreaterThan(0);
expect(result.confidence).toBeLessThanOrEqual(1);
});
it("should handle irregular cycles", () => {
const history = {
periodStarts: [
{ date: "2025-01-01" },
{ date: "2025-01-25" }, // 24 дня
{ date: "2025-02-28" }, // 33 дня
{ date: "2025-03-25" }, // 25 дней
],
};
const summary = engine.analyze(history);
expect(summary.irregular).toBe(true);
});
});
Integration Tests
describe("Cycle Prediction Integration", () => {
it("should provide consistent predictions across methods", () => {
const engine = new PredictionEngine();
const history = {
periodStarts: [
{ date: "2025-01-01" },
{ date: "2025-01-30" },
{ date: "2025-02-28" },
{ date: "2025-03-29" },
],
};
const nextPeriod = engine.predictNextPeriod(history);
const ovulation = engine.predictOvulation(history);
const fertile = engine.predictFertileWindow(history);
// Проверяем логическую связь между прогнозами
expect(nextPeriod.likely).toBeTruthy();
expect(ovulation.likely).toBeTruthy();
expect(fertile.start).toBeTruthy();
expect(fertile.end).toBeTruthy();
});
});
🚀 Performance
Optimization for Large Data Volumes
class OptimizedPredictionEngine extends PredictionEngine {
private cache = new Map<string, any>();
predictNextPeriod(history: HistoryInput): PredictionResult {
const cacheKey = this.generateCacheKey(history);
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
const result = super.predictNextPeriod(history);
this.cache.set(cacheKey, result);
return result;
}
private generateCacheKey(history: HistoryInput): string {
return JSON.stringify(history.periodStarts.map((p) => p.date));
}
clearCache(): void {
this.cache.clear();
}
}
Web Worker for Heavy Computations
// worker.ts
import { PredictionEngine } from "cyclia";
self.onmessage = (event) => {
const { history, config } = event.data;
try {
const engine = new PredictionEngine(config);
const predictions = {
nextPeriod: engine.predictNextPeriod(history),
ovulation: engine.predictOvulation(history),
fertile: engine.predictFertileWindow(history),
summary: engine.analyze(history),
};
self.postMessage({ success: true, predictions });
} catch (error) {
self.postMessage({ success: false, error: error.message });
}
};
// main.ts
const worker = new Worker("./worker.js");
worker.onmessage = (event) => {
if (event.data.success) {
setPredictions(event.data.predictions);
} else {
setError(event.data.error);
}
};
const calculatePredictions = (history: HistoryInput) => {
worker.postMessage({ history, config: { strategy: "wma" } });
};
🔒 Security and Privacy
Local Data Processing
class PrivacyAwarePredictionEngine extends PredictionEngine {
// Все вычисления происходят локально
// Данные не отправляются на сервер
predictNextPeriod(history: HistoryInput): PredictionResult {
// Локальная обработка
return super.predictNextPeriod(history);
}
}
Data Encryption
import CryptoJS from "crypto-js";
class EncryptedDataService {
private readonly SECRET_KEY = "your-secret-key";
encryptData(data: any): string {
return CryptoJS.AES.encrypt(
JSON.stringify(data),
this.SECRET_KEY
).toString();
}
decryptData(encryptedData: string): any {
const bytes = CryptoJS.AES.decrypt(encryptedData, this.SECRET_KEY);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
}
async saveEncryptedHistory(history: HistoryInput): Promise<void> {
const encrypted = this.encryptData(history);
localStorage.setItem("encrypted_cycle_data", encrypted);
}
async loadEncryptedHistory(): Promise<HistoryInput> {
const encrypted = localStorage.getItem("encrypted_cycle_data");
if (!encrypted) return { periodStarts: [] };
return this.decryptData(encrypted);
}
}
📊 Monitoring and Analytics
Usage Logging
class AnalyticsService {
logPredictionRequest(history: HistoryInput, predictions: any): void {
const event = {
timestamp: new Date().toISOString(),
dataPoints: history.periodStarts.length,
predictionType: "cycle",
confidence: predictions.nextPeriod.confidence,
irregular: predictions.summary.irregular,
};
// Отправка в аналитическую систему
this.sendAnalytics(event);
}
private sendAnalytics(event: any): void {
// Интеграция с Google Analytics, Mixpanel, etc.
if (typeof gtag !== "undefined") {
gtag("event", "cycle_prediction", event);
}
}
}
🤝 Contributing
Development Setup
git clone https://github.com/NextFutureHub/cyclia.git
cd cyclia
npm install
npm run test
Project Structure
cyclia/
├── src/
│ ├── core/ # Core logic
│ ├── plugins/ # Prediction algorithms
│ ├── utils/ # Utilities
│ ├── types.ts # TypeScript types
│ └── index.ts # Entry point
├── tests/ # Tests
├── docs/ # Documentation
└── examples/ # Usage examples
Adding New Algorithms
- Create a new class that extends
BaseRule
- Implement the
predictNextPeriod
andpredictFertility
methods - Add tests
- Update documentation
// src/plugins/yourAlgorithm.ts
import { BaseRule } from "./baseRule";
export class YourAlgorithm extends BaseRule {
readonly id = "your-algorithm";
predictNextPeriod(history, cfg, summary) {
// Your logic here
return {
likely: "2025-06-15",
window: { start: "2025-06-13", end: "2025-06-17" },
confidence: 0.8,
notes: ["your algorithm"],
};
}
}
Submitting a Pull Request
- Fork the repository
- Create a branch for your feature
- Add tests
- Update documentation
- Submit a Pull Request
📈 Roadmap
Planned Features
- [ ] Machine learning for improved accuracy
- [ ] Integration with wearable devices
- [ ] Multi-user support
- [ ] API for server-side processing
- [ ] Plugins for popular frameworks
- [ ] Integration with medical systems
Versions
- v1.0.0 - Basic functionality
📞 Support
- Email: support@cycle-predictor.com
📄 License
MIT License - see LICENSE for details.
⭐ If you like this library, consider giving it a star on GitHub!
Important: This library is for informational purposes only and does not replace medical advice. Always consult your doctor for health matters.