Skip to main content

Quick Logging & DNR Research Access System

Overview

This system enables:

  1. Quick Logging - One-button catch logging with automatic data capture from Bluetooth devices
  2. Privacy Controls - Granular control over data visibility and sharing
  3. DNR Research Access - Controlled access for Department of Natural Resources and researchers
  4. Legal Compliance - Full audit trail and consent tracking

Quick Logging Workflow

1. Start Fishing Session

var session = new FishingSession
{
UserId = userId,
Name = "Morning Trip - Lake Michigan",
StartTime = DateTime.UtcNow,
StartLatitude = 41.8781,
StartLongitude = -87.6298,
LocationName = "Lake Michigan - Port of Chicago",
GearSetupId = savedGearSetupId,
FishingMethod = FishingMethod.Trolling,
IsQuickLogSession = true,
ConnectedDeviceId = bluetoothDeviceId,
PrimaryTelemetrySource = TelemetrySource.FishHawk,
DataSharingConsent = user.DefaultDataSharingConsent,
AllowDnrAccess = user.DefaultAllowDnrAccess
};

2. Continuous Telemetry Capture

During the session, telemetry data is automatically captured:

var telemetry = new SessionTelemetry
{
FishingSessionId = session.Id,
RecordedAt = DateTime.UtcNow,
Latitude = currentLat,
Longitude = currentLon,
DepthM = fishHawkDevice.Depth,
WaterTempC = fishHawkDevice.WaterTemp,
GpsSpeedKph = gpsDevice.Speed,
HeadingDeg = gpsDevice.Heading,
Source = TelemetrySource.FishHawk
};

3. Quick Log Catch (Button Press)

When user presses quick-log button:

var logEntry = new FishingLogEntry
{
UserId = userId,
FishingSessionId = session.Id,
Date = DateOnly.FromDateTime(DateTime.UtcNow),
Time = TimeOnly.FromDateTime(DateTime.UtcNow),
CaughtAt = DateTime.UtcNow,
Latitude = currentLat, // From GPS
Longitude = currentLon, // From GPS
AccuracyMeters = gpsDevice.Accuracy,
FishingMethod = session.FishingMethod,
IsQuickLogged = true,
QuickLoggedAt = DateTime.UtcNow,
HasAutoCapturedData = true,
DataSourceDeviceId = bluetoothDeviceId,
Visibility = LogVisibility.Private, // Default to private
DataSharingConsent = session.DataSharingConsent,
AllowDnrAccess = session.AllowDnrAccess,
IncludeInMigrationAnalysis = false, // User must opt-in
IncludeInHotspotAnalysis = false, // User must opt-in
GearSetupId = session.GearSetupId
};

// Auto-capture current telemetry
var telemetry = new Telemetry
{
FishingLogEntryId = logEntry.Id,
DepthM = currentSessionTelemetry.DepthM,
WaterTempC = currentSessionTelemetry.WaterTempC,
GpsSpeedKph = currentSessionTelemetry.GpsSpeedKph,
HeadingDeg = currentSessionTelemetry.HeadingDeg,
Source = TelemetrySource.FishHawk
};

// Auto-capture current weather (from API)
var weather = new Weather
{
FishingLogEntryId = logEntry.Id,
TemperatureC = windyApi.Temperature,
WindSpeedKph = windyApi.WindSpeed,
WindDirectionDeg = windyApi.WindDirection,
PressureHpa = windyApi.Pressure,
Source = WeatherSource.Windy
};

// Catch details (needs user input or AI)
var catchDetail = new CatchDetail
{
FishingLogEntryId = logEntry.Id,
NeedsReview = true, // User must verify/complete
IsVerified = false,
// AI can attempt to identify species from photo
AiAnalyzedSpecies = aiService.IdentifySpecies(photoUrl)
};

4. User Review & Completion

User reviews quick-logged catches and completes details:

// User reviews and updates catch
catchDetail.FishSpeciesId = selectedSpeciesId;
catchDetail.LengthCm = measuredLength;
catchDetail.WeightKg = measuredWeight;
catchDetail.Condition = CatchCondition.Alive;
catchDetail.Disposition = CatchDisposition.Released;
catchDetail.NeedsReview = false;
catchDetail.IsVerified = true;

// User can update privacy settings
logEntry.Visibility = LogVisibility.Public;
logEntry.IncludeInMigrationAnalysis = true; // Opt-in for research
logEntry.IncludeInHotspotAnalysis = true; // Opt-in for hotspot data

Privacy & Data Sharing

Privacy Levels

  • Public - Visible to everyone
  • Private - Only visible to owner (and DNR if consent given)
  • Circle - Visible to specific circle members
  • Friends - Visible to friends only
  • None - No data sharing
  • Anonymous - Share anonymized data only
  • Aggregated - Share aggregated data for research
  • Full - Full data sharing (with explicit consent)
  • DNROnly - Share with DNR only

Per-Entry Override

Users can override default consent on a per-entry basis:

logEntry.DataSharingConsent = DataSharingConsent.Aggregated;
logEntry.AllowDnrAccess = true;
logEntry.IncludeInMigrationAnalysis = true;

DNR Research Access

DNR User Setup

var dnrUser = new User
{
Role = UserRole.DNR,
Email = "dnr@state.gov",
// ... other fields
};

Querying Data with DNR Access

// DNR can access all data where consent is given
var accessibleLogs = context.FishingLogEntries
.Where(fle =>
// Public logs
fle.Visibility == LogVisibility.Public ||
// Logs with DNR consent
fle.AllowDnrAccess == true ||
// Logs with data sharing consent
fle.DataSharingConsent != DataSharingConsent.None ||
// User's default consent allows DNR access
(fle.User.DefaultAllowDnrAccess && fle.DataSharingConsent == DataSharingConsent.DNROnly)
)
.ToList();

// Log the access
var accessLog = new DnrAccessLog
{
AccessorUserId = dnrUserId,
FishingLogEntryId = logEntryId,
UserId = logEntry.UserId,
AccessType = "View",
Purpose = "Migration pattern analysis",
ResearchProjectId = "2024-Salmon-Migration",
AccessedLocationData = true,
AccessedCatchData = true,
AccessedTelemetryData = true,
HasConsent = logEntry.AllowDnrAccess || logEntry.DataSharingConsent != DataSharingConsent.None,
DataSharingAgreementId = activeAgreement?.Id
};

Migration Pattern Analysis

// Query logs for migration analysis (only opted-in data)
var migrationData = context.FishingLogEntries
.Where(fle =>
fle.IncludeInMigrationAnalysis == true &&
fle.FishSpeciesId == targetSpeciesId &&
fle.Date >= startDate &&
fle.Date <= endDate
)
.Select(fle => new
{
fle.Latitude,
fle.Longitude,
fle.Date,
fle.Time,
fle.CatchDetails.FishSpecies.CommonName,
fle.Telemetry.WaterTempC,
fle.Weather.TemperatureC
})
.ToList();

// Generate migration heatmap
var heatmap = migrationData
.GroupBy(d => new { Lat = Math.Round(d.Latitude, 2), Lon = Math.Round(d.Longitude, 2) })
.Select(g => new
{
Location = g.Key,
CatchCount = g.Count(),
AvgWaterTemp = g.Average(d => d.WaterTempC),
DateRange = new { Start = g.Min(d => d.Date), End = g.Max(d => d.Date) }
})
.OrderByDescending(h => h.CatchCount)
.ToList();

Hotspot Analysis

// Query logs for hotspot analysis (only opted-in data)
var hotspotData = context.FishingLogEntries
.Where(fle =>
fle.IncludeInHotspotAnalysis == true &&
fle.Latitude >= bounds.South &&
fle.Latitude <= bounds.North &&
fle.Longitude >= bounds.West &&
fle.Longitude <= bounds.East
)
.GroupBy(fle => new
{
Lat = Math.Round(fle.Latitude, 3),
Lon = Math.Round(fle.Longitude, 3)
})
.Select(g => new
{
Location = g.Key,
TotalCatches = g.Count(),
UniqueSpecies = g.Select(c => c.CatchDetails.FishSpeciesId).Distinct().Count(),
AvgDepth = g.Average(c => c.Telemetry.DepthM),
MostCommonSpecies = g.GroupBy(c => c.CatchDetails.FishSpeciesId)
.OrderByDescending(sg => sg.Count())
.First().Key
})
.OrderByDescending(h => h.TotalCatches)
.Take(100) // Top 100 hotspots
.ToList();

Data Sharing Agreement

var agreement = new DataSharingAgreement
{
UserId = userId,
AgreementVersion = "1.0",
AgreementType = "DNR",
AgreementText = "Full agreement text...",
ConsentLevel = DataSharingConsent.DNROnly,
AllowDnrAccess = true,
AllowMigrationAnalysis = true,
AllowHotspotAnalysis = false, // User opted out of hotspot sharing
EffectiveDate = DateTime.UtcNow,
ExpirationDate = DateTime.UtcNow.AddYears(1),
Jurisdiction = "Michigan",
LegalBasis = "Conservation and fisheries management",
RequiresExplicitConsent = true,
ConsentDate = DateTime.UtcNow,
UserIpAddress = requestIpAddress,
UserAgent = requestUserAgent
};

Access Audit Trail

Every DNR access is logged:

var accessLog = new DnrAccessLog
{
AccessorUserId = dnrUserId,
FishingLogEntryId = logEntryId,
UserId = logEntry.UserId,
AccessType = "Export",
Purpose = "Annual migration report",
ResearchProjectId = "2024-Salmon-Report",
AccessedLocationData = true,
AccessedCatchData = true,
AccessedTelemetryData = true,
AccessedWeatherData = true,
HasConsent = true,
DataSharingAgreementId = agreement.Id,
AccessedAt = DateTime.UtcNow,
IpAddress = requestIpAddress,
UserAgent = requestUserAgent
};

Users can revoke consent:

var agreement = context.DataSharingAgreements
.FirstOrDefault(dsa => dsa.UserId == userId && dsa.IsActive);

if (agreement != null)
{
agreement.IsActive = false;
agreement.RevokedAt = DateTime.UtcNow;
agreement.RevocationReason = "User requested";

// Future logs won't be accessible
// Existing logs remain accessible per agreement terms
}

Best Practices

  1. Always check consent before allowing DNR access
  2. Log all access for audit trail
  3. Respect user privacy - default to private
  4. Opt-in only - never auto-enroll in data sharing
  5. Clear consent UI - make consent terms clear and understandable
  6. Regular audits - review DNR access logs regularly
  7. Data minimization - only access data needed for purpose
  8. Anonymization - when possible, use anonymized data

API Endpoints Needed

  1. POST /api/fishing-sessions - Start fishing session
  2. POST /api/fishing-sessions/{id}/quick-log - Quick log catch
  3. GET /api/fishing-sessions/{id}/catches - Get catches in session
  4. PUT /api/catches/{id}/review - Review/complete quick-logged catch
  5. GET /api/dnr/logs - DNR: Query accessible logs (with consent check)
  6. GET /api/dnr/migration-patterns - DNR: Get migration data
  7. GET /api/dnr/hotspots - DNR: Get hotspot data
  8. POST /api/data-sharing/consent - User consent to data sharing
  9. DELETE /api/data-sharing/consent - Revoke consent
  10. GET /api/data-sharing/access-logs - User view their access logs