Ver Fonte

initial

fares há 9 meses atrás
commit
95550b00db
14 ficheiros alterados com 2083 adições e 0 exclusões
  1. 3 0
      .gitignore
  2. 3 0
      CHANGELOG.md
  3. 38 0
      Dockerfile
  4. 2 0
      README.md
  5. 30 0
      analysis_options.yaml
  6. 31 0
      bin/server.dart
  7. 26 0
      docker-compose.yml
  8. 31 0
      lib/basic_auth_middleware.dart
  9. 158 0
      lib/calendar_handler.dart
  10. 1135 0
      lib/data.dart
  11. 70 0
      lib/icalendar.dart
  12. 530 0
      pubspec.lock
  13. 19 0
      pubspec.yaml
  14. 7 0
      test/icalserver_test.dart

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+# https://dart.dev/guides/libraries/private-files
+# Created by `dart pub`
+.dart_tool/

+ 3 - 0
CHANGELOG.md

@@ -0,0 +1,3 @@
+## 1.0.0
+
+- Initial version.

+ 38 - 0
Dockerfile

@@ -0,0 +1,38 @@
+FROM dart:3.3.0 AS build
+
+# Set working directory
+WORKDIR /app
+
+# Copy pubspec files
+COPY pubspec.* ./
+RUN dart pub get
+
+# Copy source code
+COPY . .
+
+# Compile to native code using AOT
+RUN dart compile exe bin/server.dart -o server
+
+# Create runtime image
+FROM debian:bookworm-slim
+
+# Install SSL certificates for HTTPS requests
+RUN apt-get update && apt-get install -y ca-certificates curl && rm -rf /var/lib/apt/lists/*
+
+# Set working directory
+WORKDIR /app
+
+# Create upload directories
+#RUN mkdir -p /app/uploaded/raw /app/uploaded/data
+
+# Copy compiled binary from build stage
+COPY --from=build /app/server /app/server
+
+# Set execute permissions
+RUN chmod +x /app/server
+
+# Expose port
+EXPOSE 8080
+
+# Start server
+CMD ["/app/server"]

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+A sample command-line application with an entrypoint in `bin/`, library code
+in `lib/`, and example unit test in `test/`.

+ 30 - 0
analysis_options.yaml

@@ -0,0 +1,30 @@
+# This file configures the static analysis results for your project (errors,
+# warnings, and lints).
+#
+# This enables the 'recommended' set of lints from `package:lints`.
+# This set helps identify many issues that may lead to problems when running
+# or consuming Dart code, and enforces writing Dart using a single, idiomatic
+# style and format.
+#
+# If you want a smaller set of lints you can change this to specify
+# 'package:lints/core.yaml'. These are just the most critical lints
+# (the recommended set includes the core lints).
+# The core lints are also what is used by pub.dev for scoring packages.
+
+include: package:lints/recommended.yaml
+
+# Uncomment the following section to specify additional rules.
+
+# linter:
+#   rules:
+#     - camel_case_types
+
+# analyzer:
+#   exclude:
+#     - path/to/excluded/files/**
+
+# For more information about the core and recommended set of lints, see
+# https://dart.dev/go/core-lints
+
+# For additional information about configuring this file, see
+# https://dart.dev/guides/language/analysis-options

+ 31 - 0
bin/server.dart

@@ -0,0 +1,31 @@
+import 'package:icalserver/basic_auth_middleware.dart';
+import 'package:icalserver/calendar_handler.dart';
+import 'package:shelf/shelf.dart';
+import 'package:shelf/shelf_io.dart' as io;
+import 'package:shelf_router/shelf_router.dart';
+
+void main() async {
+  // Create a router
+  var router = Router();
+
+  // Define a route
+  router.get('/link', (Request request) async {
+    return CalendarHandler().getCalendar(request);
+  });
+
+  // Wrap the router with middleware
+  var pipeline = Pipeline()
+      .addMiddleware(
+        basicAuthMiddleware(
+          username: 'admin',
+          password: 'secret123',
+          realm: 'My Secure API',
+        ),
+      )
+      .addHandler(router.call);
+
+  // Start the server
+  var server = await io.serve(pipeline, '0.0.0.0', 8080);
+
+  print('Server running on localhost:${server.port}');
+}

+ 26 - 0
docker-compose.yml

@@ -0,0 +1,26 @@
+services:
+  tp5-ical:
+    container_name: tp5-ical
+    build:
+      context: .
+      dockerfile: Dockerfile
+    #ports:
+    #  - "8080:8080"
+    #volumes:
+    #  - uploaded_data:/app/uploaded
+    restart: unless-stopped
+    #healthcheck:
+    #  test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/health"]
+    #  interval: 300s
+    #  timeout: 10s
+    #  retries: 3
+    #  start_period: 10s
+
+#volumes:
+#  uploaded_data:
+#    name: file_upload_uploaded_data
+
+networks:
+  default:
+    name: caddy-network
+    external: true

+ 31 - 0
lib/basic_auth_middleware.dart

@@ -0,0 +1,31 @@
+import 'package:shelf/shelf.dart';
+
+Middleware basicAuthMiddleware({
+  required String username,
+  required String password,
+  String realm = 'Secured Area',
+}) {
+  // final validAuth = 'Basic ' + base64Encode(utf8.encode('$username:$password'));
+
+  return (Handler innerHandler) {
+    return (Request request) async {
+      final authHeader = request.headers['authorization'];
+
+      // If no auth header is present or if it's invalid
+      if (authHeader == null /*|| authHeader != validAuth*/) {
+        // Return 401 with WWW-Authenticate header to trigger browser prompt
+        return Response(
+          401,
+          body: 'Authorization Required',
+          headers: {
+            'WWW-Authenticate': 'Basic realm="$realm"',
+            'Content-Type': 'text/plain',
+          },
+        );
+      }
+
+      // If auth is valid, proceed with the request
+      return await innerHandler(request);
+    };
+  };
+}

+ 158 - 0
lib/calendar_handler.dart

@@ -0,0 +1,158 @@
+import 'dart:convert';
+
+import 'package:icalserver/data.dart';
+import 'package:icalserver/icalendar.dart';
+import 'package:supabase/supabase.dart';
+import 'package:shelf/shelf.dart';
+
+class CalendarHandler {
+  SupabaseClient getSupabaseClient(Request request) {
+    // final supabaseUrl = request.headers['supabase-url'];
+    // final authHeader = request.headers['authorization'];
+    // final token = authHeader!.substring(7); // Remove 'Bearer ' prefix
+
+    final supabaseUrl = 'http://baas.fares.cyou:8000';
+    final token =
+        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q";
+    return SupabaseClient(
+      supabaseUrl,
+      token,
+    );
+  }
+
+  // Handler for the /link route
+  Future<Response> getCalendar(Request request) async {
+    final credentials = _getBasicAuthCredentials(request);
+    if (credentials == null) {
+      return Response.unauthorized('Invalid credentials');
+    }
+
+    final (username, password) = credentials;
+    if (username != password) {
+      return Response.unauthorized('Invalid credentials');
+    }
+
+    final supabase = getSupabaseClient(request);
+
+    //get licence_csv table into variable
+    final licences = (await supabase.from('licences_csv').select());
+    //print(licencesCsv);
+
+    // Execute the query
+    final rosterlist =
+        await supabase.rpc('get_crew_roster', params: {'_tlc': username});
+
+    // Handle the response
+    print('TLC: $username ...  EVENTS: ${rosterlist.length}');
+
+    // Create an iCalendar object
+    final ical = ICalendar();
+
+    if (rosterlist is List) {
+      for (var i = 0; i < rosterlist.length; i++) {
+        final event = rosterlist[i];
+        final pnleg = Pnleg(
+            date: event['date'],
+            tlc: username,
+            actype: event['actype'],
+            al: event['al'],
+            fnum: event['fnum'],
+            dep: event['dep'],
+            arr: event['des'],
+            depdate: event['ddep'],
+            deptime: event['hdep'],
+            arrdate: event['ddes'],
+            arrtime: event['hdes'],
+            label: event['label'],
+            type: event['type']);
+        final List<List<String>> crewlist = [];
+        for (var j = 0; j < (event['crew'] as List).length; j++) {
+          final crewmember = event['crew'][j] as String;
+          final crewmemberDataAc = licences.firstWhere(
+              (e) => (e['tlc'] == crewmember) && (e['ac'] == pnleg.ac),
+              orElse: () => {});
+          final crewmemberData = licences
+              .firstWhere((e) => e['tlc'] == crewmember, orElse: () => {});
+          crewlist.add([
+            '${([
+                  "F",
+                  "G"
+                ].contains(event['crewtype'][j]) ? "DH " : null) ?? (crewmemberDataAc["college"] ?? "--")}',
+            '${crewmemberData["lname"] ?? "------"}',
+            '${crewmemberData["fname"] ?? "Unknown"}'
+          ]);
+        }
+        final crewlistdisplay = (crewlist
+              ..sort((a, b) {
+                const order = {
+                  'CP': 0,
+                  'FO': 1,
+                  'PU': 2,
+                  'SE': 3,
+                  'ST': 4,
+                  'JU': 5
+                };
+                return (order[a[0]] ?? 6).compareTo(order[b[0]] ?? 6);
+              }))
+            .map((e) => "${e[0]}: ${e[1]}, ${e[2]}")
+            .join("\n");
+        switch (pnleg.dutytype) {
+          case "flight":
+            ical.addEvent(
+              summary: "${pnleg.dep}-${pnleg.arr}",
+              description: """
+Flight from ${pnleg.dep} to ${pnleg.arr}
+Flight number: ${pnleg.al}${pnleg.fnum}
+Aircraft type: ${pnleg.actype}
+
+Departure time: ${pnleg.jdep?.Hm}
+Arrival time: ${pnleg.jarr?.Hm}
+
+Crew: 
+$crewlistdisplay
+"""
+                  .replaceAll("\n", "\\n"),
+              startTime: pnleg.jdep!.dateTime,
+              endTime: pnleg.jarr!.dateTime,
+              attendees: event['crew'],
+            );
+            break;
+          case "dhflight":
+          case "dhlimo":
+          case "standby":
+          case "ground":
+          case "day":
+          default:
+        }
+      }
+    }
+
+    // Return the response with the iCalendar content
+    await supabase.dispose();
+    return Response.ok(
+      ical.serialize(),
+      headers: {'Content-Type': 'text/calendar; charset=utf-8'},
+    );
+  }
+
+  (String, String)? _getBasicAuthCredentials(Request request) {
+    // Get Authorization header
+    String authHeader = request.headers['authorization'] ?? "";
+    if (!authHeader.startsWith('Basic ')) {
+      return null;
+    }
+
+    // Extract and decode credentials
+    try {
+      String base64Credentials = authHeader.substring('Basic '.length);
+      String credentials = utf8.decode(base64.decode(base64Credentials));
+      List<String> userPass = credentials.split(':');
+
+      if (userPass.length != 2) return null;
+
+      return (userPass[0], userPass[1]);
+    } catch (e) {
+      return null;
+    }
+  }
+}

+ 1135 - 0
lib/data.dart

@@ -0,0 +1,1135 @@
+import 'dart:convert';
+
+import 'package:jiffy/jiffy.dart';
+
+class Qualif {
+  String? tlc;
+  String? lname;
+  String? mname;
+  String? fname;
+  String? date;
+  String? ac;
+  String? college;
+  String? base;
+  Qualif({
+    this.tlc,
+    this.lname,
+    this.mname,
+    this.fname,
+    this.date,
+    this.ac,
+    this.college,
+    this.base,
+  });
+
+  Qualif copyWith({
+    String? tlc,
+    String? lname,
+    String? mname,
+    String? fname,
+    String? date,
+    String? ac,
+    String? college,
+    String? base,
+  }) {
+    return Qualif(
+      tlc: tlc ?? this.tlc,
+      lname: lname ?? this.lname,
+      mname: mname ?? this.mname,
+      fname: fname ?? this.fname,
+      date: date ?? this.date,
+      ac: ac ?? this.ac,
+      college: college ?? this.college,
+      base: base ?? this.base,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return <String, dynamic>{
+      'tlc': tlc,
+      'lname': lname,
+      'mname': mname,
+      'fname': fname,
+      'date': date,
+      'ac': ac,
+      'college': college,
+      'base': base,
+    };
+  }
+
+  factory Qualif.fromList(List datalist) {
+    return Qualif(
+        tlc: datalist[0],
+        lname: datalist[1],
+        mname: datalist[2],
+        fname: datalist[3],
+        date: datalist[4],
+        ac: datalist[5],
+        college: datalist[6],
+        base: datalist[7]);
+  }
+
+  factory Qualif.fromMap(Map<String, dynamic> map) {
+    return Qualif(
+      tlc: map['tlc'] != null ? map['tlc'] as String : null,
+      lname: map['lname'] != null ? map['lname'] as String : null,
+      mname: map['mname'] != null ? map['mname'] as String : null,
+      fname: map['fname'] != null ? map['fname'] as String : null,
+      date: map['date'] != null ? map['date'] as String : null,
+      ac: map['ac'] != null ? map['ac'] as String : null,
+      college: map['college'] != null ? map['college'] as String : null,
+      base: map['base'] != null ? map['base'] as String : null,
+    );
+  }
+
+  String toJson() => json.encode(toMap());
+
+  factory Qualif.fromJson(String source) =>
+      Qualif.fromMap(json.decode(source) as Map<String, dynamic>);
+
+  @override
+  String toString() {
+    return 'Qualif(tlc: $tlc, lname: $lname, mname: $mname, fname: $fname, date: $date, ac: $ac, college: $college, base: $base)';
+  }
+
+  @override
+  bool operator ==(covariant Qualif other) {
+    if (identical(this, other)) return true;
+
+    return other.tlc == tlc &&
+        other.lname == lname &&
+        other.mname == mname &&
+        other.fname == fname &&
+        other.date == date &&
+        other.ac == ac &&
+        other.college == college &&
+        other.base == base;
+  }
+
+  @override
+  int get hashCode {
+    return tlc.hashCode ^
+        lname.hashCode ^
+        mname.hashCode ^
+        fname.hashCode ^
+        date.hashCode ^
+        ac.hashCode ^
+        college.hashCode ^
+        base.hashCode;
+  }
+}
+
+class Pnleg {
+  String? date;
+  Jiffy? get jdate => date != null
+      ? Jiffy.parse(date ?? "01/01/1970", pattern: "dd/MM/yyyy", isUtc: true)
+      : null;
+
+  String? tlc;
+  String? actype;
+  String? al;
+  String? fnum;
+  String? depdate;
+  String? deptime;
+  String? arrdate;
+  String? arrtime;
+  String? dep;
+  String? arr;
+  String? label;
+  String? type;
+  Jiffy? get jdep =>
+      deptime != null ? "$depdate $deptime".parseddmmyyyyhhmm() : null;
+  Jiffy? get jarr =>
+      arrtime != null ? "$arrdate $arrtime".parseddmmyyyyhhmm() : null;
+  String? get ac {
+    switch (actype) {
+      case "32A":
+        return "320";
+      case "32L":
+        return "320";
+      case "332":
+        return "330";
+      default:
+        return actype;
+    }
+  }
+
+  String get dutytype {
+    if (type == "L") {
+      return "flight";
+    } else if ((type == "G")) {
+      return "dhlimo";
+    } else if ((type == "F") || ((dep ?? "") != "" && (arr ?? "") != "")) {
+      return "dhflight";
+    } else if ((label?.startsWith("SBY") ?? false) || (label == "R0")) {
+      return "standby";
+    } else if ((!["OFF", "CM", "CA", "PP"].contains(label)) &&
+        (jarr != null &&
+            jdep != null &&
+            DTInterval(jdep!, jarr!).duration.inHours < 18)) {
+      return "ground";
+    } else {
+      return "day";
+    }
+  }
+
+  Pnleg({
+    this.date,
+    this.tlc,
+    this.actype,
+    this.al,
+    this.fnum,
+    this.depdate,
+    this.deptime,
+    this.arrdate,
+    this.arrtime,
+    this.dep,
+    this.arr,
+    this.label,
+    this.type,
+  });
+
+  Pnleg copyWith({
+    String? date,
+    String? tlc,
+    String? actype,
+    String? al,
+    String? fnum,
+    String? depdate,
+    String? deptime,
+    String? arrdate,
+    String? arrtime,
+    String? dep,
+    String? arr,
+    String? label,
+    String? type,
+  }) {
+    return Pnleg(
+      date: date ?? this.date,
+      tlc: tlc ?? this.tlc,
+      actype: actype ?? this.actype,
+      al: al ?? this.al,
+      fnum: fnum ?? this.fnum,
+      depdate: depdate ?? this.depdate,
+      deptime: deptime ?? this.deptime,
+      arrdate: arrdate ?? this.arrdate,
+      arrtime: arrtime ?? this.arrtime,
+      dep: dep ?? this.dep,
+      arr: arr ?? this.arr,
+      label: label ?? this.label,
+      type: type ?? this.type,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return <String, dynamic>{
+      'date': date,
+      'tlc': tlc,
+      'actype': actype,
+      'al': al,
+      'fnum': fnum,
+      'depdate': depdate,
+      'deptime': deptime,
+      'arrdate': arrdate,
+      'arrtime': arrtime,
+      'dep': dep,
+      'arr': arr,
+      'label': label,
+      'type': type,
+    };
+  }
+
+  factory Pnleg.fromList(List datalist) {
+    return Pnleg(
+        date: datalist[0],
+        tlc: datalist[1],
+        actype: datalist[2],
+        al: datalist[3],
+        fnum: datalist[4],
+        depdate: datalist[5],
+        deptime: datalist[6],
+        arrdate: datalist[7],
+        arrtime: datalist[8],
+        dep: datalist[9],
+        arr: datalist[10],
+        label: datalist[11],
+        type: datalist[12]);
+  }
+
+  factory Pnleg.fromMap(Map<String, dynamic> map) {
+    return Pnleg(
+      date: map['date'] != null ? map['date'] as String : null,
+      tlc: map['tlc'] != null ? map['tlc'] as String : null,
+      actype: map['actype'] != null ? map['actype'] as String : null,
+      al: map['al'] != null ? map['al'] as String : null,
+      fnum: map['fnum'] != null ? map['fnum'] as String : null,
+      depdate: map['depdate'] != null ? map['depdate'] as String : null,
+      deptime: map['deptime'] != null ? map['deptime'] as String : null,
+      arrdate: map['arrdate'] != null ? map['arrdate'] as String : null,
+      arrtime: map['arrtime'] != null ? map['arrtime'] as String : null,
+      dep: map['dep'] != null ? map['dep'] as String : null,
+      arr: map['arr'] != null ? map['arr'] as String : null,
+      label: map['label'] != null ? map['label'] as String : null,
+      type: map['type'] != null ? map['type'] as String : null,
+    );
+  }
+
+  String toJson() => json.encode(toMap());
+
+  factory Pnleg.fromJson(String source) =>
+      Pnleg.fromMap(json.decode(source) as Map<String, dynamic>);
+
+  @override
+  String toString() {
+    return 'Pnleg(date: $date, tlc: $tlc, actype: $actype, al: $al, fnum: $fnum, depdate: $depdate, deptime: $deptime, arrdate: $arrdate, arrtime: $arrtime, dep: $dep, arr: $arr, label: $label, type: $type)';
+  }
+
+  @override
+  bool operator ==(covariant Pnleg other) {
+    if (identical(this, other)) return true;
+
+    return other.date == date &&
+        other.tlc == tlc &&
+        other.actype == actype &&
+        other.al == al &&
+        other.fnum == fnum &&
+        other.depdate == depdate &&
+        other.deptime == deptime &&
+        other.arrdate == arrdate &&
+        other.arrtime == arrtime &&
+        other.dep == dep &&
+        other.arr == arr &&
+        other.label == label &&
+        other.type == type;
+  }
+
+  @override
+  int get hashCode {
+    return date.hashCode ^
+        tlc.hashCode ^
+        actype.hashCode ^
+        al.hashCode ^
+        fnum.hashCode ^
+        depdate.hashCode ^
+        deptime.hashCode ^
+        arrdate.hashCode ^
+        arrtime.hashCode ^
+        dep.hashCode ^
+        arr.hashCode ^
+        label.hashCode ^
+        type.hashCode;
+  }
+}
+
+//enum flt_status { sched, delayed, taxiout, enroute, landed, arrived }
+int convertTimeToMinutes(String strx) {
+  String str = strx.trim();
+  if (str.length < 5) {
+    str = str.padLeft(5, "0");
+  }
+
+  try {
+    final hours = int.parse(str.substring(0, str.length - 2));
+    final minutes = int.parse(str.substring(str.length - 2));
+
+    if (minutes >= 60) {
+      throw FormatException('Minutes must be less than 60');
+    }
+
+    return hours * 60 + minutes;
+  } catch (e) {
+    print("data: converttimetominutes: $str");
+    throw FormatException('Invalid time format. Expected hhhmm');
+  }
+}
+
+class Acleg {
+  String? LEG_NO;
+  String? FN_CARRIER;
+  String? FN_NUMBER;
+  String? FN_SUFFIX;
+  String? DAY_OF_ORIGIN;
+  String? AC_OWNER;
+  String? AC_SUBTYPE;
+  String? AC_VERSION;
+  String? AC_REGISTRATION;
+  String? DEP_AP_ACTUAL;
+  String? DEP_AP_SCHED;
+  String? DEP_DT_EST;
+  Jiffy? get jdepest =>
+      (DEP_DT_EST == null) ? null : DEP_DT_EST!.parseyyyymmddhhmm();
+  String? DEP_SCHED_DT;
+  Jiffy? get jdepsched =>
+      (DEP_SCHED_DT == null) ? null : DEP_SCHED_DT!.parseyyyymmddhhmm();
+  String? ARR_AP_ACTUAL;
+  String? ARR_AP_SCHED;
+  String? ARR_DT_EST;
+  Jiffy? get jarrest =>
+      (ARR_DT_EST == null) ? null : ARR_DT_EST!.parseyyyymmddhhmm();
+  String? ARR_SCHED_DT;
+  Jiffy? get jarrsched =>
+      (ARR_SCHED_DT == null) ? null : ARR_SCHED_DT!.parseyyyymmddhhmm();
+  String? SLOT_TIME_ACTUAL;
+  Jiffy? get slot =>
+      (SLOT_TIME_ACTUAL == null) ? null : SLOT_TIME_ACTUAL!.parseyyyymmddhhmm();
+  String? LEG_TYPE;
+  String? STATUS;
+  String? EMPLOYER_COCKPIT;
+  String? EMPLOYER_CABIN;
+  String? CYCLES;
+  String? DELAY_CODE_01;
+  String? DELAY_CODE_02;
+  String? DELAY_CODE_03;
+  String? DELAY_CODE_04;
+  String? DELAY_TIME_01;
+  String? DELAY_TIME_02;
+  String? DELAY_TIME_03;
+  String? DELAY_TIME_04;
+  String? SUBDELAY_CODE_01;
+  String? SUBDELAY_CODE_02;
+  String? SUBDELAY_CODE_03;
+  String? SUBDELAY_CODE_04;
+  List<List> get dla => [
+        [
+          SUBDELAY_CODE_01 ?? DELAY_CODE_01,
+          DELAY_TIME_01 == null
+              ? null
+              : Duration(minutes: convertTimeToMinutes(DELAY_TIME_01!))
+        ],
+        [
+          SUBDELAY_CODE_02 ?? DELAY_CODE_02,
+          DELAY_TIME_02 == null
+              ? null
+              : Duration(minutes: convertTimeToMinutes(DELAY_TIME_02!))
+        ],
+        [
+          SUBDELAY_CODE_03 ?? DELAY_CODE_03,
+          DELAY_TIME_03 == null
+              ? null
+              : Duration(minutes: convertTimeToMinutes(DELAY_TIME_03!))
+        ],
+        [
+          SUBDELAY_CODE_04 ?? DELAY_CODE_04,
+          DELAY_TIME_04 == null
+              ? null
+              : Duration(minutes: convertTimeToMinutes(DELAY_TIME_04!))
+        ],
+      ].where((e) => e.every((f) => f != null)).toList();
+  List<String> get delaycode => [
+        DELAY_CODE_01,
+        DELAY_CODE_02,
+        DELAY_CODE_03,
+        DELAY_CODE_04
+      ].nonNulls.toList();
+  List<String> get delaysubcode => [
+        SUBDELAY_CODE_01,
+        SUBDELAY_CODE_02,
+        SUBDELAY_CODE_03,
+        SUBDELAY_CODE_04
+      ].nonNulls.toList();
+  List<Duration?> get delaytime => [
+        DELAY_TIME_01 == null
+            ? null
+            : Duration(minutes: int.parse(DELAY_TIME_01!)),
+        DELAY_TIME_02 == null
+            ? null
+            : Duration(minutes: int.parse(DELAY_TIME_02!)),
+        DELAY_TIME_03 == null
+            ? null
+            : Duration(minutes: int.parse(DELAY_TIME_03!)),
+        DELAY_TIME_04 == null
+            ? null
+            : Duration(minutes: int.parse(DELAY_TIME_04!))
+      ].nonNulls.toList();
+
+  String? PAX_BOOKED_C;
+  String? PAX_BOOKED_Y;
+  String? get pax_booked => (PAX_BOOKED_C != null || PAX_BOOKED_Y != null)
+      ? "${(AC_VERSION != null && AC_VERSION!.contains("C")) ? "C${PAX_BOOKED_C ?? 0}." : ""}Y${PAX_BOOKED_Y ?? 0}"
+      : null;
+  String? PAX_BOOKED_TRS_C;
+  String? PAX_BOOKED_TRS_Y;
+  String? get pax_trs => (PAX_BOOKED_TRS_C != null || PAX_BOOKED_TRS_Y != null)
+      ? "C${PAX_BOOKED_TRS_C ?? 0}/Y${PAX_BOOKED_TRS_Y ?? 0}"
+      : null;
+  String? PAD_BOOKED_C;
+  String? PAD_BOOKED_Y;
+  String? get pad_booked => (PAD_BOOKED_C != null || PAD_BOOKED_Y != null)
+      ? "C${PAD_BOOKED_C ?? 0}/Y${PAD_BOOKED_Y ?? 0}"
+      : null;
+  String? OFFBLOCK_DT_A;
+  String? AIRBORNE_DT_A;
+  String? LANDING_DT_A;
+  String? ONBLOCK_DT_A;
+  List<Jiffy?> get blocks_a => [
+        (OFFBLOCK_DT_A ?? "").parseyyyymmddhhmm(),
+        (AIRBORNE_DT_A ?? "").parseyyyymmddhhmm(),
+        (LANDING_DT_A ?? "").parseyyyymmddhhmm(),
+        (ONBLOCK_DT_A ?? "").parseyyyymmddhhmm()
+      ];
+  String get flt_status {
+    if (blocks[3] != null) {
+      return "Arrived";
+    } else if (blocks[2] != null) {
+      return "Landed";
+    } else if (blocks[1] != null) {
+      return "Inflight";
+    } else if (blocks[0] != null) {
+      return "Taxiout";
+    } else if (jdepest != null &&
+        jarrsched != null &&
+        jdepest!.isAfter(jdepsched!)) {
+      return "Delayed";
+    } else {
+      return "Sched";
+    }
+  }
+
+  String? OFFBLOCK_DT_F;
+  String? AIRBORNE_DT_F;
+  String? LANDING_DT_F;
+  String? ONBLOCK_DT_F;
+  List<Jiffy?> get blocks_f => [
+        (OFFBLOCK_DT_F ?? "").parseyyyymmddhhmm(),
+        (AIRBORNE_DT_F ?? "").parseyyyymmddhhmm(),
+        (LANDING_DT_F ?? "").parseyyyymmddhhmm(),
+        (ONBLOCK_DT_F ?? "").parseyyyymmddhhmm()
+      ];
+
+  String? OFFBLOCK_DT_M;
+  String? AIRBORNE_DT_M;
+  String? LANDING_DT_M;
+  String? ONBLOCK_DT_M;
+  List<Jiffy?> get blocks_m => [
+        (OFFBLOCK_DT_M ?? "").parseyyyymmddhhmm(),
+        (AIRBORNE_DT_M ?? "").parseyyyymmddhhmm(),
+        (LANDING_DT_M ?? "").parseyyyymmddhhmm(),
+        (ONBLOCK_DT_M ?? "").parseyyyymmddhhmm()
+      ];
+  List<Jiffy?> get blocks => [
+        blocks_m[0] ?? blocks_a[0] ?? blocks_f[0],
+        blocks_m[1] ?? blocks_a[1] ?? blocks_f[1],
+        blocks_m[2] ?? blocks_a[2] ?? blocks_f[2],
+        blocks_m[3] ?? blocks_a[3] ?? blocks_f[3]
+      ];
+  Jiffy? get jdep => blocks[0] ?? jdepest ?? jdepsched;
+  Jiffy? get jarr =>
+      blocks[3] ??
+      blocks[2]?.add(minutes: 5) ??
+      (eet == null ? null : blocks[1]?.addDuration(eet!).add(minutes: 8)) ??
+      (eet == null
+          ? null
+          : blocks[0]?.add(minutes: 5).addDuration(eet!).add(minutes: 8)) ??
+      (eet == null
+          ? null
+          : jdep?.addDuration(eet!).add(minutes: 8).add(minutes: 5)) ??
+      jarrest ??
+      jarrsched;
+  String? EET;
+  Duration? get eet => EET == null ? null : Duration(minutes: int.parse(EET!));
+  Acleg({
+    this.LEG_NO,
+    this.FN_CARRIER,
+    this.FN_NUMBER,
+    this.FN_SUFFIX,
+    this.DAY_OF_ORIGIN,
+    this.AC_OWNER,
+    this.AC_SUBTYPE,
+    this.AC_VERSION,
+    this.AC_REGISTRATION,
+    this.DEP_AP_ACTUAL,
+    this.DEP_AP_SCHED,
+    this.DEP_DT_EST,
+    this.DEP_SCHED_DT,
+    this.ARR_AP_ACTUAL,
+    this.ARR_AP_SCHED,
+    this.ARR_DT_EST,
+    this.ARR_SCHED_DT,
+    this.SLOT_TIME_ACTUAL,
+    this.LEG_TYPE,
+    this.STATUS,
+    this.EMPLOYER_COCKPIT,
+    this.EMPLOYER_CABIN,
+    this.CYCLES,
+    this.DELAY_CODE_01,
+    this.DELAY_CODE_02,
+    this.DELAY_CODE_03,
+    this.DELAY_CODE_04,
+    this.DELAY_TIME_01,
+    this.DELAY_TIME_02,
+    this.DELAY_TIME_03,
+    this.DELAY_TIME_04,
+    this.SUBDELAY_CODE_01,
+    this.SUBDELAY_CODE_02,
+    this.SUBDELAY_CODE_03,
+    this.SUBDELAY_CODE_04,
+    this.PAX_BOOKED_C,
+    this.PAX_BOOKED_Y,
+    this.PAX_BOOKED_TRS_C,
+    this.PAX_BOOKED_TRS_Y,
+    this.PAD_BOOKED_C,
+    this.PAD_BOOKED_Y,
+    this.OFFBLOCK_DT_A,
+    this.AIRBORNE_DT_A,
+    this.LANDING_DT_A,
+    this.ONBLOCK_DT_A,
+    this.OFFBLOCK_DT_F,
+    this.AIRBORNE_DT_F,
+    this.LANDING_DT_F,
+    this.ONBLOCK_DT_F,
+    this.OFFBLOCK_DT_M,
+    this.AIRBORNE_DT_M,
+    this.LANDING_DT_M,
+    this.ONBLOCK_DT_M,
+    this.EET,
+  });
+
+  Acleg copyWith({
+    String? LEG_NO,
+    String? FN_CARRIER,
+    String? FN_NUMBER,
+    String? FN_SUFFIX,
+    String? DAY_OF_ORIGIN,
+    String? AC_OWNER,
+    String? AC_SUBTYPE,
+    String? AC_VERSION,
+    String? AC_REGISTRATION,
+    String? DEP_AP_ACTUAL,
+    String? DEP_AP_SCHED,
+    String? DEP_DT_EST,
+    String? DEP_SCHED_DT,
+    String? ARR_AP_ACTUAL,
+    String? ARR_AP_SCHED,
+    String? ARR_DT_EST,
+    String? ARR_SCHED_DT,
+    String? SLOT_TIME_ACTUAL,
+    String? LEG_TYPE,
+    String? STATUS,
+    String? EMPLOYER_COCKPIT,
+    String? EMPLOYER_CABIN,
+    String? CYCLES,
+    String? DELAY_CODE_01,
+    String? DELAY_CODE_02,
+    String? DELAY_CODE_03,
+    String? DELAY_CODE_04,
+    String? DELAY_TIME_01,
+    String? DELAY_TIME_02,
+    String? DELAY_TIME_03,
+    String? DELAY_TIME_04,
+    String? SUBDELAY_CODE_01,
+    String? SUBDELAY_CODE_02,
+    String? SUBDELAY_CODE_03,
+    String? SUBDELAY_CODE_04,
+    String? PAX_BOOKED_C,
+    String? PAX_BOOKED_Y,
+    String? PAX_BOOKED_TRS_C,
+    String? PAX_BOOKED_TRS_Y,
+    String? PAD_BOOKED_C,
+    String? PAD_BOOKED_Y,
+    String? OFFBLOCK_DT_A,
+    String? AIRBORNE_DT_A,
+    String? LANDING_DT_A,
+    String? ONBLOCK_DT_A,
+    String? OFFBLOCK_DT_F,
+    String? AIRBORNE_DT_F,
+    String? LANDING_DT_F,
+    String? ONBLOCK_DT_F,
+    String? OFFBLOCK_DT_M,
+    String? AIRBORNE_DT_M,
+    String? LANDING_DT_M,
+    String? ONBLOCK_DT_M,
+    String? EET,
+  }) {
+    return Acleg(
+      LEG_NO: LEG_NO ?? this.LEG_NO,
+      FN_CARRIER: FN_CARRIER ?? this.FN_CARRIER,
+      FN_NUMBER: FN_NUMBER ?? this.FN_NUMBER,
+      FN_SUFFIX: FN_SUFFIX ?? this.FN_SUFFIX,
+      DAY_OF_ORIGIN: DAY_OF_ORIGIN ?? this.DAY_OF_ORIGIN,
+      AC_OWNER: AC_OWNER ?? this.AC_OWNER,
+      AC_SUBTYPE: AC_SUBTYPE ?? this.AC_SUBTYPE,
+      AC_VERSION: AC_VERSION ?? this.AC_VERSION,
+      AC_REGISTRATION: AC_REGISTRATION ?? this.AC_REGISTRATION,
+      DEP_AP_ACTUAL: DEP_AP_ACTUAL ?? this.DEP_AP_ACTUAL,
+      DEP_AP_SCHED: DEP_AP_SCHED ?? this.DEP_AP_SCHED,
+      DEP_DT_EST: DEP_DT_EST ?? this.DEP_DT_EST,
+      DEP_SCHED_DT: DEP_SCHED_DT ?? this.DEP_SCHED_DT,
+      ARR_AP_ACTUAL: ARR_AP_ACTUAL ?? this.ARR_AP_ACTUAL,
+      ARR_AP_SCHED: ARR_AP_SCHED ?? this.ARR_AP_SCHED,
+      ARR_DT_EST: ARR_DT_EST ?? this.ARR_DT_EST,
+      ARR_SCHED_DT: ARR_SCHED_DT ?? this.ARR_SCHED_DT,
+      SLOT_TIME_ACTUAL: SLOT_TIME_ACTUAL ?? this.SLOT_TIME_ACTUAL,
+      LEG_TYPE: LEG_TYPE ?? this.LEG_TYPE,
+      STATUS: STATUS ?? this.STATUS,
+      EMPLOYER_COCKPIT: EMPLOYER_COCKPIT ?? this.EMPLOYER_COCKPIT,
+      EMPLOYER_CABIN: EMPLOYER_CABIN ?? this.EMPLOYER_CABIN,
+      CYCLES: CYCLES ?? this.CYCLES,
+      DELAY_CODE_01: DELAY_CODE_01 ?? this.DELAY_CODE_01,
+      DELAY_CODE_02: DELAY_CODE_02 ?? this.DELAY_CODE_02,
+      DELAY_CODE_03: DELAY_CODE_03 ?? this.DELAY_CODE_03,
+      DELAY_CODE_04: DELAY_CODE_04 ?? this.DELAY_CODE_04,
+      DELAY_TIME_01: DELAY_TIME_01 ?? this.DELAY_TIME_01,
+      DELAY_TIME_02: DELAY_TIME_02 ?? this.DELAY_TIME_02,
+      DELAY_TIME_03: DELAY_TIME_03 ?? this.DELAY_TIME_03,
+      DELAY_TIME_04: DELAY_TIME_04 ?? this.DELAY_TIME_04,
+      SUBDELAY_CODE_01: SUBDELAY_CODE_01 ?? this.SUBDELAY_CODE_01,
+      SUBDELAY_CODE_02: SUBDELAY_CODE_02 ?? this.SUBDELAY_CODE_02,
+      SUBDELAY_CODE_03: SUBDELAY_CODE_03 ?? this.SUBDELAY_CODE_03,
+      SUBDELAY_CODE_04: SUBDELAY_CODE_04 ?? this.SUBDELAY_CODE_04,
+      PAX_BOOKED_C: PAX_BOOKED_C ?? this.PAX_BOOKED_C,
+      PAX_BOOKED_Y: PAX_BOOKED_Y ?? this.PAX_BOOKED_Y,
+      PAX_BOOKED_TRS_C: PAX_BOOKED_TRS_C ?? this.PAX_BOOKED_TRS_C,
+      PAX_BOOKED_TRS_Y: PAX_BOOKED_TRS_Y ?? this.PAX_BOOKED_TRS_Y,
+      PAD_BOOKED_C: PAD_BOOKED_C ?? this.PAD_BOOKED_C,
+      PAD_BOOKED_Y: PAD_BOOKED_Y ?? this.PAD_BOOKED_Y,
+      OFFBLOCK_DT_A: OFFBLOCK_DT_A ?? this.OFFBLOCK_DT_A,
+      AIRBORNE_DT_A: AIRBORNE_DT_A ?? this.AIRBORNE_DT_A,
+      LANDING_DT_A: LANDING_DT_A ?? this.LANDING_DT_A,
+      ONBLOCK_DT_A: ONBLOCK_DT_A ?? this.ONBLOCK_DT_A,
+      OFFBLOCK_DT_F: OFFBLOCK_DT_F ?? this.OFFBLOCK_DT_F,
+      AIRBORNE_DT_F: AIRBORNE_DT_F ?? this.AIRBORNE_DT_F,
+      LANDING_DT_F: LANDING_DT_F ?? this.LANDING_DT_F,
+      ONBLOCK_DT_F: ONBLOCK_DT_F ?? this.ONBLOCK_DT_F,
+      OFFBLOCK_DT_M: OFFBLOCK_DT_M ?? this.OFFBLOCK_DT_M,
+      AIRBORNE_DT_M: AIRBORNE_DT_M ?? this.AIRBORNE_DT_M,
+      LANDING_DT_M: LANDING_DT_M ?? this.LANDING_DT_M,
+      ONBLOCK_DT_M: ONBLOCK_DT_M ?? this.ONBLOCK_DT_M,
+      EET: EET ?? this.EET,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return <String, dynamic>{
+      'LEG_NO': LEG_NO,
+      'FN_CARRIER': FN_CARRIER,
+      'FN_NUMBER': FN_NUMBER,
+      'FN_SUFFIX': FN_SUFFIX,
+      'DAY_OF_ORIGIN': DAY_OF_ORIGIN,
+      'AC_OWNER': AC_OWNER,
+      'AC_SUBTYPE': AC_SUBTYPE,
+      'AC_VERSION': AC_VERSION,
+      'AC_REGISTRATION': AC_REGISTRATION,
+      'DEP_AP_ACTUAL': DEP_AP_ACTUAL,
+      'DEP_AP_SCHED': DEP_AP_SCHED,
+      'DEP_DT_EST': DEP_DT_EST,
+      'DEP_SCHED_DT': DEP_SCHED_DT,
+      'ARR_AP_ACTUAL': ARR_AP_ACTUAL,
+      'ARR_AP_SCHED': ARR_AP_SCHED,
+      'ARR_DT_EST': ARR_DT_EST,
+      'ARR_SCHED_DT': ARR_SCHED_DT,
+      'SLOT_TIME_ACTUAL': SLOT_TIME_ACTUAL,
+      'LEG_TYPE': LEG_TYPE,
+      'STATUS': STATUS,
+      'EMPLOYER_COCKPIT': EMPLOYER_COCKPIT,
+      'EMPLOYER_CABIN': EMPLOYER_CABIN,
+      'CYCLES': CYCLES,
+      'DELAY_CODE_01': DELAY_CODE_01,
+      'DELAY_CODE_02': DELAY_CODE_02,
+      'DELAY_CODE_03': DELAY_CODE_03,
+      'DELAY_CODE_04': DELAY_CODE_04,
+      'DELAY_TIME_01': DELAY_TIME_01,
+      'DELAY_TIME_02': DELAY_TIME_02,
+      'DELAY_TIME_03': DELAY_TIME_03,
+      'DELAY_TIME_04': DELAY_TIME_04,
+      'SUBDELAY_CODE_01': SUBDELAY_CODE_01,
+      'SUBDELAY_CODE_02': SUBDELAY_CODE_02,
+      'SUBDELAY_CODE_03': SUBDELAY_CODE_03,
+      'SUBDELAY_CODE_04': SUBDELAY_CODE_04,
+      'PAX_BOOKED_C': PAX_BOOKED_C,
+      'PAX_BOOKED_Y': PAX_BOOKED_Y,
+      'PAX_BOOKED_TRS_C': PAX_BOOKED_TRS_C,
+      'PAX_BOOKED_TRS_Y': PAX_BOOKED_TRS_Y,
+      'PAD_BOOKED_C': PAD_BOOKED_C,
+      'PAD_BOOKED_Y': PAD_BOOKED_Y,
+      'OFFBLOCK_DT_A': OFFBLOCK_DT_A,
+      'AIRBORNE_DT_A': AIRBORNE_DT_A,
+      'LANDING_DT_A': LANDING_DT_A,
+      'ONBLOCK_DT_A': ONBLOCK_DT_A,
+      'OFFBLOCK_DT_F': OFFBLOCK_DT_F,
+      'AIRBORNE_DT_F': AIRBORNE_DT_F,
+      'LANDING_DT_F': LANDING_DT_F,
+      'ONBLOCK_DT_F': ONBLOCK_DT_F,
+      'OFFBLOCK_DT_M': OFFBLOCK_DT_M,
+      'AIRBORNE_DT_M': AIRBORNE_DT_M,
+      'LANDING_DT_M': LANDING_DT_M,
+      'ONBLOCK_DT_M': ONBLOCK_DT_M,
+      'EET': EET,
+    };
+  }
+
+  factory Acleg.fromList(List datalist) {
+    //print(datalist);
+    if (datalist.length >= 54) {
+      return Acleg(
+          LEG_NO: datalist[0],
+          FN_CARRIER: datalist[1],
+          FN_NUMBER: datalist[2],
+          FN_SUFFIX: datalist[3],
+          DAY_OF_ORIGIN: datalist[4],
+          AC_OWNER: datalist[5],
+          AC_SUBTYPE: datalist[6],
+          AC_VERSION: datalist[7],
+          AC_REGISTRATION: datalist[8],
+          DEP_AP_ACTUAL: datalist[9],
+          DEP_AP_SCHED: datalist[10],
+          DEP_DT_EST: datalist[11],
+          DEP_SCHED_DT: datalist[12],
+          ARR_AP_ACTUAL: datalist[13],
+          ARR_AP_SCHED: datalist[14],
+          ARR_DT_EST: datalist[15],
+          ARR_SCHED_DT: datalist[16],
+          SLOT_TIME_ACTUAL: datalist[17],
+          LEG_TYPE: datalist[18],
+          STATUS: datalist[19],
+          EMPLOYER_COCKPIT: datalist[20],
+          EMPLOYER_CABIN: datalist[21],
+          CYCLES: datalist[22],
+          DELAY_CODE_01: datalist[23],
+          DELAY_CODE_02: datalist[24],
+          DELAY_CODE_03: datalist[25],
+          DELAY_CODE_04: datalist[26],
+          DELAY_TIME_01: datalist[27],
+          DELAY_TIME_02: datalist[28],
+          DELAY_TIME_03: datalist[29],
+          DELAY_TIME_04: datalist[30],
+          SUBDELAY_CODE_01: datalist[31],
+          SUBDELAY_CODE_02: datalist[32],
+          SUBDELAY_CODE_03: datalist[33],
+          SUBDELAY_CODE_04: datalist[34],
+          PAX_BOOKED_C: datalist[35],
+          PAX_BOOKED_Y: datalist[36],
+          PAX_BOOKED_TRS_C: datalist[37],
+          PAX_BOOKED_TRS_Y: datalist[38],
+          PAD_BOOKED_C: datalist[39],
+          PAD_BOOKED_Y: datalist[40],
+          OFFBLOCK_DT_A: datalist[41],
+          AIRBORNE_DT_A: datalist[42],
+          LANDING_DT_A: datalist[43],
+          ONBLOCK_DT_A: datalist[44],
+          OFFBLOCK_DT_F: datalist[45],
+          AIRBORNE_DT_F: datalist[46],
+          LANDING_DT_F: datalist[47],
+          ONBLOCK_DT_F: datalist[48],
+          OFFBLOCK_DT_M: datalist[49],
+          AIRBORNE_DT_M: datalist[50],
+          LANDING_DT_M: datalist[51],
+          ONBLOCK_DT_M: datalist[52],
+          EET: datalist[53]);
+    } else {
+      return Acleg();
+    }
+  }
+
+  factory Acleg.fromMap(Map<String, dynamic> map) {
+    return Acleg(
+      LEG_NO: map['LEG_NO'] != null ? map['LEG_NO'] as String : null,
+      FN_CARRIER:
+          map['FN_CARRIER'] != null ? map['FN_CARRIER'] as String : null,
+      FN_NUMBER: map['FN_NUMBER'] != null ? map['FN_NUMBER'] as String : null,
+      FN_SUFFIX: map['FN_SUFFIX'] != null ? map['FN_SUFFIX'] as String : null,
+      DAY_OF_ORIGIN:
+          map['DAY_OF_ORIGIN'] != null ? map['DAY_OF_ORIGIN'] as String : null,
+      AC_OWNER: map['AC_OWNER'] != null ? map['AC_OWNER'] as String : null,
+      AC_SUBTYPE:
+          map['AC_SUBTYPE'] != null ? map['AC_SUBTYPE'] as String : null,
+      AC_VERSION:
+          map['AC_VERSION'] != null ? map['AC_VERSION'] as String : null,
+      AC_REGISTRATION: map['AC_REGISTRATION'] != null
+          ? map['AC_REGISTRATION'] as String
+          : null,
+      DEP_AP_ACTUAL:
+          map['DEP_AP_ACTUAL'] != null ? map['DEP_AP_ACTUAL'] as String : null,
+      DEP_AP_SCHED:
+          map['DEP_AP_SCHED'] != null ? map['DEP_AP_SCHED'] as String : null,
+      DEP_DT_EST:
+          map['DEP_DT_EST'] != null ? map['DEP_DT_EST'] as String : null,
+      DEP_SCHED_DT:
+          map['DEP_SCHED_DT'] != null ? map['DEP_SCHED_DT'] as String : null,
+      ARR_AP_ACTUAL:
+          map['ARR_AP_ACTUAL'] != null ? map['ARR_AP_ACTUAL'] as String : null,
+      ARR_AP_SCHED:
+          map['ARR_AP_SCHED'] != null ? map['ARR_AP_SCHED'] as String : null,
+      ARR_DT_EST:
+          map['ARR_DT_EST'] != null ? map['ARR_DT_EST'] as String : null,
+      ARR_SCHED_DT:
+          map['ARR_SCHED_DT'] != null ? map['ARR_SCHED_DT'] as String : null,
+      SLOT_TIME_ACTUAL: map['SLOT_TIME_ACTUAL'] != null
+          ? map['SLOT_TIME_ACTUAL'] as String
+          : null,
+      LEG_TYPE: map['LEG_TYPE'] != null ? map['LEG_TYPE'] as String : null,
+      EMPLOYER_COCKPIT: map['EMPLOYER_COCKPIT'] != null
+          ? map['EMPLOYER_COCKPIT'] as String
+          : null,
+      EMPLOYER_CABIN: map['EMPLOYER_CABIN'] != null
+          ? map['EMPLOYER_CABIN'] as String
+          : null,
+//      CYCLES: map['CYCLES'] != null ? map['CYCLES'] as String : null,
+      DELAY_CODE_01:
+          map['DELAY_CODE_01'] != null ? map['DELAY_CODE_01'] as String : null,
+      DELAY_CODE_02:
+          map['DELAY_CODE_02'] != null ? map['DELAY_CODE_02'] as String : null,
+      DELAY_CODE_03:
+          map['DELAY_CODE_03'] != null ? map['DELAY_CODE_03'] as String : null,
+      DELAY_CODE_04:
+          map['DELAY_CODE_04'] != null ? map['DELAY_CODE_04'] as String : null,
+      DELAY_TIME_01:
+          map['DELAY_TIME_01'] != null ? map['DELAY_TIME_01'] as String : null,
+      DELAY_TIME_02:
+          map['DELAY_TIME_02'] != null ? map['DELAY_TIME_02'] as String : null,
+      DELAY_TIME_03:
+          map['DELAY_TIME_03'] != null ? map['DELAY_TIME_03'] as String : null,
+      DELAY_TIME_04:
+          map['DELAY_TIME_04'] != null ? map['DELAY_TIME_04'] as String : null,
+      SUBDELAY_CODE_01: map['SUBDELAY_CODE_01'] != null
+          ? map['SUBDELAY_CODE_01'] as String
+          : null,
+      SUBDELAY_CODE_02: map['SUBDELAY_CODE_02'] != null
+          ? map['SUBDELAY_CODE_02'] as String
+          : null,
+      SUBDELAY_CODE_03: map['SUBDELAY_CODE_03'] != null
+          ? map['SUBDELAY_CODE_03'] as String
+          : null,
+      SUBDELAY_CODE_04: map['SUBDELAY_CODE_04'] != null
+          ? map['SUBDELAY_CODE_04'] as String
+          : null,
+      PAX_BOOKED_C:
+          map['PAX_BOOKED_C'] != null ? map['PAX_BOOKED_C'] as String : null,
+      PAX_BOOKED_Y:
+          map['PAX_BOOKED_Y'] != null ? map['PAX_BOOKED_Y'] as String : null,
+      PAX_BOOKED_TRS_C: map['PAX_BOOKED_TRS_C'] != null
+          ? map['PAX_BOOKED_TRS_C'] as String
+          : null,
+      PAX_BOOKED_TRS_Y: map['PAX_BOOKED_TRS_Y'] != null
+          ? map['PAX_BOOKED_TRS_Y'] as String
+          : null,
+      PAD_BOOKED_C:
+          map['PAD_BOOKED_C'] != null ? map['PAD_BOOKED_C'] as String : null,
+      PAD_BOOKED_Y:
+          map['PAD_BOOKED_Y'] != null ? map['PAD_BOOKED_Y'] as String : null,
+      OFFBLOCK_DT_A:
+          map['OFFBLOCK_DT_A'] != null ? map['OFFBLOCK_DT_A'] as String : null,
+      AIRBORNE_DT_A:
+          map['AIRBORNE_DT_A'] != null ? map['AIRBORNE_DT_A'] as String : null,
+      LANDING_DT_A:
+          map['LANDING_DT_A'] != null ? map['LANDING_DT_A'] as String : null,
+      ONBLOCK_DT_A:
+          map['ONBLOCK_DT_A'] != null ? map['ONBLOCK_DT_A'] as String : null,
+      OFFBLOCK_DT_F:
+          map['OFFBLOCK_DT_F'] != null ? map['OFFBLOCK_DT_F'] as String : null,
+      AIRBORNE_DT_F:
+          map['AIRBORNE_DT_F'] != null ? map['AIRBORNE_DT_F'] as String : null,
+      LANDING_DT_F:
+          map['LANDING_DT_F'] != null ? map['LANDING_DT_F'] as String : null,
+      ONBLOCK_DT_F:
+          map['ONBLOCK_DT_F'] != null ? map['ONBLOCK_DT_F'] as String : null,
+      OFFBLOCK_DT_M:
+          map['OFFBLOCK_DT_M'] != null ? map['OFFBLOCK_DT_M'] as String : null,
+      AIRBORNE_DT_M:
+          map['AIRBORNE_DT_M'] != null ? map['AIRBORNE_DT_M'] as String : null,
+      LANDING_DT_M:
+          map['LANDING_DT_M'] != null ? map['LANDING_DT_M'] as String : null,
+      ONBLOCK_DT_M:
+          map['ONBLOCK_DT_M'] != null ? map['ONBLOCK_DT_M'] as String : null,
+      EET: map['EET'] != null ? map['EET'] as String : null,
+    );
+  }
+
+  String toJson() => json.encode(toMap());
+
+  factory Acleg.fromJson(String source) =>
+      Acleg.fromMap(json.decode(source) as Map<String, dynamic>);
+
+  @override
+  String toString() {
+    return 'Acleg(LEG_NO: $LEG_NO, FN_CARRIER: $FN_CARRIER, FN_NUMBER: $FN_NUMBER, FN_SUFFIX: $FN_SUFFIX, DAY_OF_ORIGIN: $DAY_OF_ORIGIN, AC_OWNER: $AC_OWNER, AC_SUBTYPE: $AC_SUBTYPE, AC_VERSION: $AC_VERSION, AC_REGISTRATION: $AC_REGISTRATION, DEP_AP_ACTUAL: $DEP_AP_ACTUAL, DEP_AP_SCHED: $DEP_AP_SCHED, DEP_DT_EST: $DEP_DT_EST, DEP_SCHED_DT: $DEP_SCHED_DT, ARR_AP_ACTUAL: $ARR_AP_ACTUAL, ARR_AP_SCHED: $ARR_AP_SCHED, ARR_DT_EST: $ARR_DT_EST, ARR_SCHED_DT: $ARR_SCHED_DT, SLOT_TIME_ACTUAL: $SLOT_TIME_ACTUAL, LEG_TYPE: $LEG_TYPE, EMPLOYER_COCKPIT: $EMPLOYER_COCKPIT, EMPLOYER_CABIN: $EMPLOYER_CABIN, CYCLES: $CYCLES, DELAY_CODE_01: $DELAY_CODE_01, DELAY_CODE_02: $DELAY_CODE_02, DELAY_CODE_03: $DELAY_CODE_03, DELAY_CODE_04: $DELAY_CODE_04, DELAY_TIME_01: $DELAY_TIME_01, DELAY_TIME_02: $DELAY_TIME_02, DELAY_TIME_03: $DELAY_TIME_03, DELAY_TIME_04: $DELAY_TIME_04, SUBDELAY_CODE_01: $SUBDELAY_CODE_01, SUBDELAY_CODE_02: $SUBDELAY_CODE_02, SUBDELAY_CODE_03: $SUBDELAY_CODE_03, SUBDELAY_CODE_04: $SUBDELAY_CODE_04, PAX_BOOKED_C: $PAX_BOOKED_C, PAX_BOOKED_Y: $PAX_BOOKED_Y, PAX_BOOKED_TRS_C: $PAX_BOOKED_TRS_C, PAX_BOOKED_TRS_Y: $PAX_BOOKED_TRS_Y, PAD_BOOKED_C: $PAD_BOOKED_C, PAD_BOOKED_Y: $PAD_BOOKED_Y, OFFBLOCK_DT_A: $OFFBLOCK_DT_A, AIRBORNE_DT_A: $AIRBORNE_DT_A, LANDING_DT_A: $LANDING_DT_A, ONBLOCK_DT_A: $ONBLOCK_DT_A, OFFBLOCK_DT_F: $OFFBLOCK_DT_F, AIRBORNE_DT_F: $AIRBORNE_DT_F, LANDING_DT_F: $LANDING_DT_F, ONBLOCK_DT_F: $ONBLOCK_DT_F, OFFBLOCK_DT_M: $OFFBLOCK_DT_M, AIRBORNE_DT_M: $AIRBORNE_DT_M, LANDING_DT_M: $LANDING_DT_M, ONBLOCK_DT_M: $ONBLOCK_DT_M, EET: $EET)';
+  }
+
+  @override
+  bool operator ==(covariant Acleg other) {
+    if (identical(this, other)) return true;
+
+    return other.LEG_NO == LEG_NO &&
+        other.FN_CARRIER == FN_CARRIER &&
+        other.FN_NUMBER == FN_NUMBER &&
+        other.FN_SUFFIX == FN_SUFFIX &&
+        other.DAY_OF_ORIGIN == DAY_OF_ORIGIN &&
+        other.AC_OWNER == AC_OWNER &&
+        other.AC_SUBTYPE == AC_SUBTYPE &&
+        other.AC_VERSION == AC_VERSION &&
+        other.AC_REGISTRATION == AC_REGISTRATION &&
+        other.DEP_AP_ACTUAL == DEP_AP_ACTUAL &&
+        other.DEP_AP_SCHED == DEP_AP_SCHED &&
+        other.DEP_DT_EST == DEP_DT_EST &&
+        other.DEP_SCHED_DT == DEP_SCHED_DT &&
+        other.ARR_AP_ACTUAL == ARR_AP_ACTUAL &&
+        other.ARR_AP_SCHED == ARR_AP_SCHED &&
+        other.ARR_DT_EST == ARR_DT_EST &&
+        other.ARR_SCHED_DT == ARR_SCHED_DT &&
+        other.SLOT_TIME_ACTUAL == SLOT_TIME_ACTUAL &&
+        other.LEG_TYPE == LEG_TYPE &&
+        other.EMPLOYER_COCKPIT == EMPLOYER_COCKPIT &&
+        other.EMPLOYER_CABIN == EMPLOYER_CABIN &&
+        other.CYCLES == CYCLES &&
+        other.DELAY_CODE_01 == DELAY_CODE_01 &&
+        other.DELAY_CODE_02 == DELAY_CODE_02 &&
+        other.DELAY_CODE_03 == DELAY_CODE_03 &&
+        other.DELAY_CODE_04 == DELAY_CODE_04 &&
+        other.DELAY_TIME_01 == DELAY_TIME_01 &&
+        other.DELAY_TIME_02 == DELAY_TIME_02 &&
+        other.DELAY_TIME_03 == DELAY_TIME_03 &&
+        other.DELAY_TIME_04 == DELAY_TIME_04 &&
+        other.SUBDELAY_CODE_01 == SUBDELAY_CODE_01 &&
+        other.SUBDELAY_CODE_02 == SUBDELAY_CODE_02 &&
+        other.SUBDELAY_CODE_03 == SUBDELAY_CODE_03 &&
+        other.SUBDELAY_CODE_04 == SUBDELAY_CODE_04 &&
+        other.PAX_BOOKED_C == PAX_BOOKED_C &&
+        other.PAX_BOOKED_Y == PAX_BOOKED_Y &&
+        other.PAX_BOOKED_TRS_C == PAX_BOOKED_TRS_C &&
+        other.PAX_BOOKED_TRS_Y == PAX_BOOKED_TRS_Y &&
+        other.PAD_BOOKED_C == PAD_BOOKED_C &&
+        other.PAD_BOOKED_Y == PAD_BOOKED_Y &&
+        other.OFFBLOCK_DT_A == OFFBLOCK_DT_A &&
+        other.AIRBORNE_DT_A == AIRBORNE_DT_A &&
+        other.LANDING_DT_A == LANDING_DT_A &&
+        other.ONBLOCK_DT_A == ONBLOCK_DT_A &&
+        other.OFFBLOCK_DT_F == OFFBLOCK_DT_F &&
+        other.AIRBORNE_DT_F == AIRBORNE_DT_F &&
+        other.LANDING_DT_F == LANDING_DT_F &&
+        other.ONBLOCK_DT_F == ONBLOCK_DT_F &&
+        other.OFFBLOCK_DT_M == OFFBLOCK_DT_M &&
+        other.AIRBORNE_DT_M == AIRBORNE_DT_M &&
+        other.LANDING_DT_M == LANDING_DT_M &&
+        other.ONBLOCK_DT_M == ONBLOCK_DT_M &&
+        other.EET == EET;
+  }
+
+  @override
+  int get hashCode {
+    return LEG_NO.hashCode ^
+        FN_CARRIER.hashCode ^
+        FN_NUMBER.hashCode ^
+        FN_SUFFIX.hashCode ^
+        DAY_OF_ORIGIN.hashCode ^
+        AC_OWNER.hashCode ^
+        AC_SUBTYPE.hashCode ^
+        AC_VERSION.hashCode ^
+        AC_REGISTRATION.hashCode ^
+        DEP_AP_ACTUAL.hashCode ^
+        DEP_AP_SCHED.hashCode ^
+        DEP_DT_EST.hashCode ^
+        DEP_SCHED_DT.hashCode ^
+        ARR_AP_ACTUAL.hashCode ^
+        ARR_AP_SCHED.hashCode ^
+        ARR_DT_EST.hashCode ^
+        ARR_SCHED_DT.hashCode ^
+        SLOT_TIME_ACTUAL.hashCode ^
+        LEG_TYPE.hashCode ^
+        EMPLOYER_COCKPIT.hashCode ^
+        EMPLOYER_CABIN.hashCode ^
+        CYCLES.hashCode ^
+        DELAY_CODE_01.hashCode ^
+        DELAY_CODE_02.hashCode ^
+        DELAY_CODE_03.hashCode ^
+        DELAY_CODE_04.hashCode ^
+        DELAY_TIME_01.hashCode ^
+        DELAY_TIME_02.hashCode ^
+        DELAY_TIME_03.hashCode ^
+        DELAY_TIME_04.hashCode ^
+        SUBDELAY_CODE_01.hashCode ^
+        SUBDELAY_CODE_02.hashCode ^
+        SUBDELAY_CODE_03.hashCode ^
+        SUBDELAY_CODE_04.hashCode ^
+        PAX_BOOKED_C.hashCode ^
+        PAX_BOOKED_Y.hashCode ^
+        PAX_BOOKED_TRS_C.hashCode ^
+        PAX_BOOKED_TRS_Y.hashCode ^
+        PAD_BOOKED_C.hashCode ^
+        PAD_BOOKED_Y.hashCode ^
+        OFFBLOCK_DT_A.hashCode ^
+        AIRBORNE_DT_A.hashCode ^
+        LANDING_DT_A.hashCode ^
+        ONBLOCK_DT_A.hashCode ^
+        OFFBLOCK_DT_F.hashCode ^
+        AIRBORNE_DT_F.hashCode ^
+        LANDING_DT_F.hashCode ^
+        ONBLOCK_DT_F.hashCode ^
+        OFFBLOCK_DT_M.hashCode ^
+        AIRBORNE_DT_M.hashCode ^
+        LANDING_DT_M.hashCode ^
+        ONBLOCK_DT_M.hashCode ^
+        EET.hashCode;
+  }
+}
+
+extension StringExtensions on String {
+  String capitalize() {
+    if (isEmpty) {
+      return this;
+    } else {
+      return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
+    }
+  }
+
+  String capitalizeword() {
+    return split(' ').map((word) => word.capitalize()).join(' ');
+  }
+
+  Jiffy? parseddmmyyyyhhmm() => length >= 15
+      ? Jiffy.parse(
+          "${substring(6, 10)}-${substring(3, 5)}-${substring(0, 2)} ${substring(11, 13)}:${substring(13, 15)}",
+          pattern: 'yyyy-MM-dd HH:mm',
+          isUtc: true)
+      : null;
+  Jiffy? parseyyyymmddhhmm() => length >= 16
+      ? Jiffy.parse(this,
+          pattern: 'yyyy-MM-dd HH:mm:ss',
+          // "${substring(6, 10)}-${substring(3, 5)}-${substring(0, 2)} ${substring(11, 13)}${substring(13, 16)}",
+          // pattern: 'yyyy-MM-dd HH:mm',
+          isUtc: true)
+      : null;
+}
+
+class DTInterval {
+  late Jiffy start;
+  late Jiffy end;
+  DTInterval(this.start, this.end);
+  @override
+  String toString() =>
+      "<${start.format(pattern: "ddMMMyy HH:mm")} - ${end.format(pattern: "ddMMMyy HH:mm")}>";
+
+  bool include(Jiffy x) {
+    return x.isSameOrAfter(start) && x.isSameOrBefore(end);
+  }
+
+  bool isOverlap(DTInterval x) {
+    return x.start.isSameOrBefore(end) && x.end.isSameOrAfter(start);
+  }
+
+  bool contains(DTInterval x) {
+    return include(x.start) && include(x.end);
+  }
+
+  List<DTInterval> minus(DTInterval x) {
+    if (!isOverlap(x)) {
+      return [this];
+    } else if (x.include(start) && x.include(end)) {
+      return [];
+    } else if (x.include(start)) {
+      return [DTInterval(x.end, end)];
+    } else if (x.include(end)) {
+      return [DTInterval(start, x.start)];
+    } else {
+      return [DTInterval(start, x.start), DTInterval(x.end, end)];
+    }
+  }
+
+  Duration get duration => end.dateTime.difference(start.dateTime);
+
+  bool isEmpty() {
+    return start.isSameOrAfter(end);
+  }
+
+  DTInterval toUtc() {
+    return DTInterval(start.toUtc(), end.toUtc());
+  }
+}

+ 70 - 0
lib/icalendar.dart

@@ -0,0 +1,70 @@
+class ICalendar {
+  final List<ICalendarEvent> _events = [];
+
+  void addEvent({
+    required String summary,
+    required String description,
+    required DateTime startTime,
+    required DateTime endTime,
+    required List<dynamic> attendees,
+  }) {
+    _events.add(
+      ICalendarEvent(
+        summary: summary,
+        description: description,
+        startTime: startTime,
+        endTime: endTime,
+        attendees: attendees,
+      ),
+    );
+  }
+
+  String serialize() {
+    final dtstamp = DateTime.now();
+    final buffer = StringBuffer();
+    buffer.writeln('BEGIN:VCALENDAR');
+    buffer.writeln('VERSION:2.0');
+    buffer.writeln('PRODID:-//Your Organization//Your Product//EN');
+
+    for (final event in _events) {
+      buffer.writeln('BEGIN:VEVENT');
+      buffer.writeln(
+          'UID:${DateTime.now().millisecondsSinceEpoch}-${event.hashCode}');
+      buffer.writeln('DTSTAMP:${_formatDateTime(dtstamp)}');
+      buffer.writeln('DTSTART:${_formatDateTime(event.startTime)}');
+      buffer.writeln('DTEND:${_formatDateTime(event.endTime)}');
+      buffer.writeln('SUMMARY:${event.summary}');
+      buffer.writeln('DESCRIPTION:${event.description}');
+
+      for (final attendee in event.attendees) {
+        buffer.writeln(
+            'ATTENDEE;CN="${attendee.toString()}";ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:test@me.com');
+      }
+
+      buffer.writeln('END:VEVENT');
+    }
+
+    buffer.writeln('END:VCALENDAR');
+    return buffer.toString();
+  }
+
+  String _formatDateTime(DateTime dt) {
+    return '${dt.toUtc().toString().replaceAll('-', '').replaceAll(':', '').replaceAll(' ', 'T').split('.')[0]}Z';
+  }
+}
+
+class ICalendarEvent {
+  final String summary;
+  final String description;
+  final DateTime startTime;
+  final DateTime endTime;
+  final List<dynamic> attendees;
+
+  ICalendarEvent({
+    required this.summary,
+    required this.description,
+    required this.startTime,
+    required this.endTime,
+    required this.attendees,
+  });
+}

+ 530 - 0
pubspec.lock

@@ -0,0 +1,530 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+  _fe_analyzer_shared:
+    dependency: transitive
+    description:
+      name: _fe_analyzer_shared
+      sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "79.0.0"
+  _macros:
+    dependency: transitive
+    description: dart
+    source: sdk
+    version: "0.3.3"
+  analyzer:
+    dependency: transitive
+    description:
+      name: analyzer
+      sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643
+      url: "https://pub.dev"
+    source: hosted
+    version: "7.2.0"
+  args:
+    dependency: transitive
+    description:
+      name: args
+      sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.6.0"
+  async:
+    dependency: transitive
+    description:
+      name: async
+      sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.12.0"
+  boolean_selector:
+    dependency: transitive
+    description:
+      name: boolean_selector
+      sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.2"
+  clock:
+    dependency: transitive
+    description:
+      name: clock
+      sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.2"
+  collection:
+    dependency: transitive
+    description:
+      name: collection
+      sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.19.1"
+  convert:
+    dependency: transitive
+    description:
+      name: convert
+      sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.2"
+  coverage:
+    dependency: transitive
+    description:
+      name: coverage
+      sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.11.1"
+  crypto:
+    dependency: transitive
+    description:
+      name: crypto
+      sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.6"
+  file:
+    dependency: transitive
+    description:
+      name: file
+      sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+      url: "https://pub.dev"
+    source: hosted
+    version: "7.0.1"
+  frontend_server_client:
+    dependency: transitive
+    description:
+      name: frontend_server_client
+      sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.0.0"
+  functions_client:
+    dependency: transitive
+    description:
+      name: functions_client
+      sha256: "61597ed93be197b1be6387855e4b760e6aac2355fcfc4df6d20d2b4579982158"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.0"
+  glob:
+    dependency: transitive
+    description:
+      name: glob
+      sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.3"
+  gotrue:
+    dependency: transitive
+    description:
+      name: gotrue
+      sha256: d6362dff9a54f8c1c372bb137c858b4024c16407324d34e6473e59623c9b9f50
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.11.1"
+  http:
+    dependency: transitive
+    description:
+      name: http
+      sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.3.0"
+  http_methods:
+    dependency: transitive
+    description:
+      name: http_methods
+      sha256: "6bccce8f1ec7b5d701e7921dca35e202d425b57e317ba1a37f2638590e29e566"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.1"
+  http_multi_server:
+    dependency: transitive
+    description:
+      name: http_multi_server
+      sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.2.2"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+      url: "https://pub.dev"
+    source: hosted
+    version: "4.1.2"
+  intl:
+    dependency: transitive
+    description:
+      name: intl
+      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.20.2"
+  io:
+    dependency: transitive
+    description:
+      name: io
+      sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.5"
+  jiffy:
+    dependency: "direct main"
+    description:
+      name: jiffy
+      sha256: "1c1b86459969ff9f32dc5b0ffe392f1e08181e66396cf9dd8fa7c90552a691af"
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.3.2"
+  js:
+    dependency: transitive
+    description:
+      name: js
+      sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.7.1"
+  jwt_decode:
+    dependency: transitive
+    description:
+      name: jwt_decode
+      sha256: d2e9f68c052b2225130977429d30f187aa1981d789c76ad104a32243cfdebfbb
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.3.1"
+  lints:
+    dependency: "direct dev"
+    description:
+      name: lints
+      sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.1.1"
+  logging:
+    dependency: transitive
+    description:
+      name: logging
+      sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.3.0"
+  macros:
+    dependency: transitive
+    description:
+      name: macros
+      sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.1.3-main.0"
+  matcher:
+    dependency: transitive
+    description:
+      name: matcher
+      sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.12.17"
+  meta:
+    dependency: transitive
+    description:
+      name: meta
+      sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.16.0"
+  mime:
+    dependency: transitive
+    description:
+      name: mime
+      sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.0"
+  node_preamble:
+    dependency: transitive
+    description:
+      name: node_preamble
+      sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.2"
+  package_config:
+    dependency: transitive
+    description:
+      name: package_config
+      sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.1"
+  path:
+    dependency: transitive
+    description:
+      name: path
+      sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.9.1"
+  pool:
+    dependency: transitive
+    description:
+      name: pool
+      sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.5.1"
+  postgrest:
+    dependency: transitive
+    description:
+      name: postgrest
+      sha256: b74dc0f57b5dca5ce9f57a54b08110bf41d6fc8a0483c0fec10c79e9aa0fb2bb
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.1"
+  pub_semver:
+    dependency: transitive
+    description:
+      name: pub_semver
+      sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.5"
+  realtime_client:
+    dependency: transitive
+    description:
+      name: realtime_client
+      sha256: "1bfcb7455fdcf15953bf18ac2817634ea5b8f7f350c7e8c9873141a3ee2c3e9c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.1"
+  retry:
+    dependency: transitive
+    description:
+      name: retry
+      sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.2"
+  rxdart:
+    dependency: transitive
+    description:
+      name: rxdart
+      sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.28.0"
+  shelf:
+    dependency: "direct main"
+    description:
+      name: shelf
+      sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.2"
+  shelf_packages_handler:
+    dependency: transitive
+    description:
+      name: shelf_packages_handler
+      sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.2"
+  shelf_router:
+    dependency: "direct main"
+    description:
+      name: shelf_router
+      sha256: f5e5d492440a7fb165fe1e2e1a623f31f734d3370900070b2b1e0d0428d59864
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.4"
+  shelf_static:
+    dependency: transitive
+    description:
+      name: shelf_static
+      sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.3"
+  shelf_web_socket:
+    dependency: transitive
+    description:
+      name: shelf_web_socket
+      sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.1"
+  source_map_stack_trace:
+    dependency: transitive
+    description:
+      name: source_map_stack_trace
+      sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.2"
+  source_maps:
+    dependency: transitive
+    description:
+      name: source_maps
+      sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.10.13"
+  source_span:
+    dependency: transitive
+    description:
+      name: source_span
+      sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.10.1"
+  stack_trace:
+    dependency: transitive
+    description:
+      name: stack_trace
+      sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.12.1"
+  storage_client:
+    dependency: transitive
+    description:
+      name: storage_client
+      sha256: d80d34f0aa60e5199646bc301f5750767ee37310c2ecfe8d4bbdd29351e09ab0
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.3.0"
+  stream_channel:
+    dependency: transitive
+    description:
+      name: stream_channel
+      sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.1.4"
+  string_scanner:
+    dependency: transitive
+    description:
+      name: string_scanner
+      sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.1"
+  supabase:
+    dependency: "direct main"
+    description:
+      name: supabase
+      sha256: "270f63cd87a16578fee87e40cbf61062e8cdbce68d5e723e665f4651d70ddd8c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.6.2"
+  term_glyph:
+    dependency: transitive
+    description:
+      name: term_glyph
+      sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.2"
+  test:
+    dependency: "direct dev"
+    description:
+      name: test
+      sha256: "8391fbe68d520daf2314121764d38e37f934c02fd7301ad18307bd93bd6b725d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.25.14"
+  test_api:
+    dependency: transitive
+    description:
+      name: test_api
+      sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.7.4"
+  test_core:
+    dependency: transitive
+    description:
+      name: test_core
+      sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.6.8"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.4.0"
+  vm_service:
+    dependency: transitive
+    description:
+      name: vm_service
+      sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
+      url: "https://pub.dev"
+    source: hosted
+    version: "15.0.0"
+  watcher:
+    dependency: transitive
+    description:
+      name: watcher
+      sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.1"
+  web:
+    dependency: transitive
+    description:
+      name: web
+      sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.0"
+  web_socket:
+    dependency: transitive
+    description:
+      name: web_socket
+      sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.1.6"
+  web_socket_channel:
+    dependency: transitive
+    description:
+      name: web_socket_channel
+      sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.2"
+  webkit_inspection_protocol:
+    dependency: transitive
+    description:
+      name: webkit_inspection_protocol
+      sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.1"
+  yaml:
+    dependency: transitive
+    description:
+      name: yaml
+      sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.1.3"
+  yet_another_json_isolate:
+    dependency: transitive
+    description:
+      name: yet_another_json_isolate
+      sha256: "56155e9e0002cc51ea7112857bbcdc714d4c35e176d43e4d3ee233009ff410c9"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.3"
+sdks:
+  dart: ">=3.6.0 <4.0.0"

+ 19 - 0
pubspec.yaml

@@ -0,0 +1,19 @@
+name: icalserver
+description: A sample command-line application.
+version: 1.0.0
+# repository: https://github.com/my_org/my_repo
+
+environment:
+  sdk: ">=3.0.0"
+
+# Add regular dependencies here.
+dependencies:
+  shelf: #^1.4.0
+  shelf_router: #^1.1.0
+  #ical: #^3.0.0
+  supabase: #^2.6.2
+  jiffy: #^6.0.0
+
+dev_dependencies:
+  lints: #^5.0.0
+  test: #^1.24.0

+ 7 - 0
test/icalserver_test.dart

@@ -0,0 +1,7 @@
+import 'package:test/test.dart';
+
+void main() {
+  test('calculate', () {
+    expect((), 42);
+  });
+}