movieWithUserDataFromTurtle static method

Map<String, dynamic>? movieWithUserDataFromTurtle(
  1. String ttlContent
)

Parses a single movie with user data from TTL content. Returns a map containing the movie, rating, and comment.

Implementation

static Map<String, dynamic>? movieWithUserDataFromTurtle(String ttlContent) {
  try {
    // First try to parse from JSON backup for compatibility.

    final movieJsonMatch =
        RegExp(r'# JSON_MOVIE_DATA: (.+)').firstMatch(ttlContent);
    final userDataJsonMatch =
        RegExp(r'# JSON_USER_DATA: (.+)').firstMatch(ttlContent);

    if (movieJsonMatch != null && userDataJsonMatch != null) {
      final movieJsonData = movieJsonMatch.group(1)!;
      final userDataJsonData = userDataJsonMatch.group(1)!;

      final movieData = jsonDecode(movieJsonData) as Map<String, dynamic>;
      final userData = jsonDecode(userDataJsonData) as Map<String, dynamic>;

      return {
        'movie': Movie.fromJson(movieData),
        'rating': userData['rating'],
        'comment': userData['comment'],
      };
    }

    // Parse using proper RDF if no JSON backup.

    final triples = turtleToTripleMap(ttlContent);
    Movie? movie;
    double? rating;
    String? comment;

    // Find movie, rating, and comment resources.

    for (final subject in triples.keys) {
      final predicates = triples[subject]!;
      final typeValues =
          predicates['http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] ?? [];

      // Check for movie.

      final isMovie = typeValues.any(
        (type) =>
            type.toString().contains('Movie') ||
            type == 'http://schema.org/Movie' ||
            type == '#Movie',
      );

      if (isMovie && movie == null) {
        movie = _extractMovieFromTriples(predicates);

        // Also check for rating and comment in the same movie resource (new ontology format).

        if (rating == null) {
          final contentRatingKey = predicates.keys.firstWhere(
            (key) => key.toString().contains('contentRating'),
            orElse: () => '',
          );
          if (contentRatingKey.isNotEmpty &&
              predicates[contentRatingKey]!.isNotEmpty) {
            final rawValue = predicates[contentRatingKey]!.first.toString();
            rating = double.tryParse(rawValue);
          }
        }

        if (comment == null) {
          final commentKey = predicates.keys.firstWhere(
            (key) => key.toString().contains('comment'),
            orElse: () => '',
          );
          if (commentKey.isNotEmpty && predicates[commentKey]!.isNotEmpty) {
            comment = predicates[commentKey]!.first.toString();
          }
        }
      }

      // Check for rating (old format compatibility).

      final isRating = typeValues.any(
        (type) => type.toString().contains('Rating') || type == '#Rating',
      );

      if (isRating && rating == null) {
        final valueKey = predicates.keys.firstWhere(
          (key) => key.toString().contains('value'),
          orElse: () => '',
        );
        if (valueKey.isNotEmpty && predicates[valueKey]!.isNotEmpty) {
          final rawValue = predicates[valueKey]!.first.toString();
          rating = double.tryParse(rawValue);
        }
      }

      // Check for comment (old format compatibility).

      final isComment = typeValues.any(
        (type) => type.toString().contains('Comment') || type == '#Comment',
      );

      if (isComment && comment == null) {
        final textKey = predicates.keys.firstWhere(
          (key) => key.toString().contains('text'),
          orElse: () => '',
        );
        if (textKey.isNotEmpty && predicates[textKey]!.isNotEmpty) {
          comment = predicates[textKey]!.first.toString();
        }
      }
    }

    if (movie != null) {
      return {
        'movie': movie,
        'rating': rating,
        'comment': comment,
      };
    }

    return null;
  } catch (e) {
    // Don't log parsing errors - they're often due to expected empty/missing files.

    return null;
  }
}