Нашел задание для проверки возможностей ИИ
От: Shmj Ниоткуда  
Дата: 04.01.25 14:02
Оценка: :)
Помните собирали такие задания. Но то для серьезных вещей, а у меня более мелкое, близкое нам — кодинг.

Придумать не получалось — а вот когда затребовала среда — оно как бы само нашлось. Просто взял из рабочего проекта задание, которое можно выделить и реализовать без какой-либо связи с проектом.

Почему это не тривиально? Т.к. либо слишком простое придумывается (на что есть готовый ответ в сети), либо слишком сложное, для которого нужно погружать в детали проекта. А тут как бы вещь в себе, но при этом ответа в сети готового не найдете. Код компилируется, не требует зависимостей от библиотек.

Итак, полный текст, который можно скопировать и дать GPT:

  Для GPT

Язык Dart. Требуется реализовать методы retrieveItems и getItemFromCache менеджера кеширования — класс ObjectDdCacheManager.

Условия:

1. Учитывать FetchDataPolicy — если never, то запрос к API не выполняем, возвращаем то что есть в кеше (или ничего). Если onEmptyCache — то делаем запрос к API только в случае, если в кеше нет данных. Если always — то всегда делаем запрос к API и обновляем данные кеша.

2. Учитывать CacheUpdateMode. Если entities — то только заменяем сущности. Если box — то удаляем старую коробку целиком со всеми объектами.

3. Учитывать фильтр Filter. Если установлен, то запрос к базе делаем через getBeyKey (вид фильтра entityKey) или select (вид range). Фильтр применяем и для удаления сущностей в режиме entities — удаляем все сущности из коробки, которые соответствуют фильтру (а если фильтр не установлен — то просто заменяем сущности методом putAll).

4. Учитывать флаг useMemoryCache. Если установлен, то кроме базы данных используем дополнительно и кеш в памяти — _memoryCache. С ним работаем так же как с базой (можно добавить обертку по аналогии с моим моком ObjectDdMock). Если данные есть в _memoryCache и не нужно обновлять данные — в базу не лезем.

Для удобства, чтобы код собирался, добавлен раздел с моками — его трогать не нужно.


// ****************************** MOCKS  ******************************

class ErrorInfo {
  final int errorCode;
  ErrorInfo(this.errorCode);
}

class ResultOrError<TResult> {
  late bool isSuccess;
  TResult? result;
  ErrorInfo? error;

  ResultOrError._fromResult(this.result) {
    isSuccess = true;
    error = null;
  }

  ResultOrError._fromError(this.error) {
    isSuccess = false;
    result = null;
  }

  factory ResultOrError.fromResult(TResult result) =>
      ResultOrError._fromResult(result);
  factory ResultOrError.fromError(ErrorInfo error) =>
      ResultOrError._fromError(error);
}

class ApiObject1 {
  final Map<String, String> items = {"item1": "value1", "item2": "value2"};
}

abstract class DataEntity {
  String get key;
}

class DataEntity1 extends DataEntity {
  String name;
  String value;

  @override
  String get key => name;

  DataEntity1(this.name, this.value);
}

class Model1 {
  final String item;
  Model1(this.item);
}

class ObjectDdMock<TDataEntity extends DataEntity> {
  String get boxKey => TDataEntity.runtimeType.toString();

  static final Map<String, Map<String, dynamic>> _boxes = {};

  List<TDataEntity> getAll() {
    return _boxes[boxKey]?.values.cast<TDataEntity>().toList() ?? [];
  }

  TDataEntity? getBeyKey(String key) {
    final box = _boxes[boxKey];

    if (null == box) return null;
    return box[key] as TDataEntity;
  }

  List<TDataEntity> select(String startKey, String endKey) {
    final box = _boxes[boxKey];

    if (null == box) return [];

    final allEntities = getAll();
    List<String> keys = allEntities
        .map((e) => e.key)
        .where((k) => k.compareTo(startKey) >= 0 && k.compareTo(endKey) <= 0)
        .toList();
    keys.sort();

    List<TDataEntity> result = [];

    for (final key in keys) {
      result.add(box[key]!);
    }

    return result;
  }

  void putAll(List<TDataEntity> entities) {
    for (final entity in entities) {
      put(entity);
    }
  }

  void put(TDataEntity entity) {
    if (!_boxes.containsKey(boxKey)) {
      _boxes[boxKey] = {entity.key: entity};
    } else {
      _boxes[boxKey]![entity.key] = entity;
    }
  }

  void deleteBox() {
    if (_boxes.containsKey(boxKey)) {
      _boxes.remove(boxKey);
    }
  }

  void deleteEntities(List<TDataEntity> entities) {
    final box = _boxes[boxKey];

    if (null != box) {
      for (final entity in entities) {
        box.remove(entity.key);
      }
    }
  }
}

// ****************************** END MOCKS  ******************************

enum FetchDataPolicy { never, onEmptyCache, always }

enum CacheUpdateMode { entities, box }

enum FilterKind { entityKey, range }

abstract class Filter {
  FilterKind get kind;
}

class FilterByKey implements Filter {
  String key;

  @override
  FilterKind get kind => FilterKind.entityKey;

  FilterByKey(this.key);
}

class FilterByRange implements Filter {
  String startKey;
  String endKey;

  @override
  FilterKind get kind => FilterKind.range;

  FilterByRange({required this.startKey, required this.endKey});
}

abstract class CacheManager<TApiObject, TDataEntity extends DataEntity,
    TModel> {
  final Future<ResultOrError<TApiObject>> Function() fetchFromApi;
  final List<TDataEntity> Function(TApiObject) transformToDataEntities;
  final TModel Function(TDataEntity) transformToModel;

  final String boxName;
  final bool useMemoryCache;

  CacheManager(
      {required this.fetchFromApi,
      required this.transformToDataEntities,
      required this.transformToModel,
      required this.boxName,
      this.useMemoryCache = false});

  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter});

  Future<TModel?> getItemFromCache(String key);
}

final Map<String, Map<String, dynamic>> _memoryCache = {};

class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName});

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) {
    // TODO: implement retrieveItems
    throw UnimplementedError();
  }

  @override
  Future<TModel?> getItemFromCache(String key) {
    // TODO: implement getItemFromCache
    throw UnimplementedError();
  }
}

void main() async {
  final cacheManager = ObjectDdCacheManager(
      fetchFromApi: () async {
        return ResultOrError.fromResult(ApiObject1());
      },
      transformToDataEntities: (apiObject) {
        return apiObject.items.keys
            .map((k) => DataEntity1(k, apiObject.items[k]!))
            .toList();
      },
      transformToModel: (dataEntity) {
        return Model1(dataEntity.value);
      },
      boxName: 'box1');

  final items = await cacheManager.retrieveItems();

  print(items.result!.length);
}



Код с возможностью запуска тут: http://tpcg.io/_WD2ZDE

Вот такой результат выдает

  GPT-4o
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager({
    required super.fetchFromApi,
    required super.transformToDataEntities,
    required super.transformToModel,
    required super.boxName,
  });

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems({
    FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
    CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
    Filter? filter,
  }) async {
    final memoryCache = _memoryCache[boxName];
    final ObjectDdMock<TDataEntity> dbCache = ObjectDdMock<TDataEntity>();
    List<TDataEntity> entities;

    // Работа с кешем в памяти
    if (useMemoryCache && memoryCache != null) {
      entities = _filterEntities(memoryCache.values.cast<TDataEntity>(), filter);
      if (fetchDataPolicy == FetchDataPolicy.never || (fetchDataPolicy == FetchDataPolicy.onEmptyCache && entities.isNotEmpty)) {
        return ResultOrError.fromResult(entities.map(transformToModel).toList());
      }
    }

    // Работа с кешем в базе
    entities = _filterEntities(dbCache.getAll(), filter);
    if (fetchDataPolicy == FetchDataPolicy.never) {
      return ResultOrError.fromResult(entities.map(transformToModel).toList());
    }

    if (fetchDataPolicy == FetchDataPolicy.onEmptyCache && entities.isNotEmpty) {
      return ResultOrError.fromResult(entities.map(transformToModel).toList());
    }

    // Запрос данных из API
    final apiResult = await fetchFromApi();
    if (!apiResult.isSuccess) {
      return ResultOrError.fromError(apiResult.error!);
    }

    final newEntities = transformToDataEntities(apiResult.result!);
    if (cacheUpdateMode == CacheUpdateMode.entities) {
      if (filter != null) {
        final filteredEntities = _filterEntities(dbCache.getAll(), filter);
        dbCache.deleteEntities(filteredEntities);
      }
      dbCache.putAll(newEntities);
    } else if (cacheUpdateMode == CacheUpdateMode.box) {
      dbCache.deleteBox();
      dbCache.putAll(newEntities);
    }

    if (useMemoryCache) {
      _memoryCache[boxName] = {for (var e in newEntities) e.key: e};
    }

    return ResultOrError.fromResult(newEntities.map(transformToModel).toList());
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    if (useMemoryCache) {
      final memoryCache = _memoryCache[boxName];
      if (memoryCache != null && memoryCache.containsKey(key)) {
        return transformToModel(memoryCache[key] as TDataEntity);
      }
    }

    final ObjectDdMock<TDataEntity> dbCache = ObjectDdMock<TDataEntity>();
    final entity = dbCache.getBeyKey(key);
    if (entity != null) {
      if (useMemoryCache) {
        _memoryCache[boxName] ??= {};
        _memoryCache[boxName]![key] = entity;
      }
      return transformToModel(entity);
    }

    return null;
  }

  List<TDataEntity> _filterEntities(List<TDataEntity> entities, Filter? filter) {
    if (filter == null) return entities;

    if (filter.kind == FilterKind.entityKey) {
      final key = (filter as FilterByKey).key;
      return entities.where((e) => e.key == key).toList();
    } else if (filter.kind == FilterKind.range) {
      final startKey = (filter as FilterByRange).startKey;
      final endKey = filter.endKey;
      return entities.where((e) => e.key.compareTo(startKey) >= 0 && e.key.compareTo(endKey) <= 0).toList();
    }

    return entities;
  }
}


Тут: https://chatgpt.com/share/67793e85-4a44-8000-b3e3-fa47a8d2663f

Вот o1-preview через API — ответ стоил $0.14

  o1-preview
class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity,
    TModel> extends CacheManager<TApiObject, TDataEntity, TModel> {
  // Define the boxKey using TDataEntity's type
  final String boxKey = TDataEntity.toString();

  // Create an instance of ObjectDdMock for database operations
  final ObjectDdMock<TDataEntity> _database = ObjectDdMock<TDataEntity>();

  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName,
      super.useMemoryCache = false});

  // Helper method to get data from cache (memory or database)
  Future<List<TDataEntity>> _getDataFromCache(Filter? filter) async {
    if (useMemoryCache) {
      // Try to get data from _memoryCache
      final box = _memoryCache[boxKey];
      if (box != null) {
        if (filter == null) {
          // Return all data
          return box.values.cast<TDataEntity>().toList();
        } else if (filter.kind == FilterKind.entityKey) {
          FilterByKey filterByKey = filter as FilterByKey;
          final entity = box[filterByKey.key] as TDataEntity?;
          return entity != null ? [entity] : [];
        } else if (filter.kind == FilterKind.range) {
          FilterByRange filterByRange = filter as FilterByRange;
          final entities = box.values
              .cast<TDataEntity>()
              .where((e) =>
                  e.key.compareTo(filterByRange.startKey) >= 0 &&
                  e.key.compareTo(filterByRange.endKey) <= 0)
              .toList();
          return entities;
        }
      }
    }
    // Get data from database
    if (filter == null) {
      return _database.getAll();
    } else if (filter.kind == FilterKind.entityKey) {
      FilterByKey filterByKey = filter as FilterByKey;
      final entity = _database.getBeyKey(filterByKey.key);
      return entity != null ? [entity] : [];
    } else if (filter.kind == FilterKind.range) {
      FilterByRange filterByRange = filter as FilterByRange;
      return _database.select(filterByRange.startKey, filterByRange.endKey);
    }
    return [];
  }

  // Helper method to store data into cache (memory and database)
  Future<void> _storeDataInCache(List<TDataEntity> entities,
      CacheUpdateMode cacheUpdateMode, Filter? filter) async {
    if (cacheUpdateMode == CacheUpdateMode.box) {
      if (useMemoryCache) {
        // Remove the box from _memoryCache and add entities
        _memoryCache[boxKey] = {for (var e in entities) e.key: e};
      }
      // Delete box from database and add entities
      _database.deleteBox();
      _database.putAll(entities);
    } else if (cacheUpdateMode == CacheUpdateMode.entities) {
      if (filter != null) {
        // Delete entities matching the filter
        if (useMemoryCache) {
          final box = _memoryCache[boxKey];
          if (box != null) {
            if (filter.kind == FilterKind.entityKey) {
              FilterByKey filterByKey = filter as FilterByKey;
              box.remove(filterByKey.key);
            } else if (filter.kind == FilterKind.range) {
              FilterByRange filterByRange = filter as FilterByRange;
              final keysToRemove = box.keys
                  .where((k) =>
                      k.compareTo(filterByRange.startKey) >= 0 &&
                      k.compareTo(filterByRange.endKey) <= 0)
                  .toList();
              for (final k in keysToRemove) {
                box.remove(k);
              }
            }
          }
        }
        // Delete entities from the database
        if (filter.kind == FilterKind.entityKey) {
          FilterByKey filterByKey = filter as FilterByKey;
          final entity = _database.getBeyKey(filterByKey.key);
          if (entity != null) {
            _database.deleteEntities([entity]);
          }
        } else if (filter.kind == FilterKind.range) {
          FilterByRange filterByRange = filter as FilterByRange;
          final entitiesToDelete =
              _database.select(filterByRange.startKey, filterByRange.endKey);
          _database.deleteEntities(entitiesToDelete);
        }
      }
      // Add new entities
      if (useMemoryCache) {
        final box = _memoryCache.putIfAbsent(boxKey, () => {});
        for (var e in entities) {
          box[e.key] = e;
        }
      }
      _database.putAll(entities);
    }
  }

  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) async {
    try {
      bool needToFetch = false;
      List<TDataEntity> cachedEntities = await _getDataFromCache(filter);

      if (fetchDataPolicy == FetchDataPolicy.always) {
        needToFetch = true;
      } else if (fetchDataPolicy == FetchDataPolicy.onEmptyCache &&
          cachedEntities.isEmpty) {
        needToFetch = true;
      } else if (fetchDataPolicy == FetchDataPolicy.never) {
        needToFetch = false;
      }

      if (needToFetch) {
        // Fetch from API
        final result = await fetchFromApi();
        if (!result.isSuccess) {
          return ResultOrError.fromError(result.error!);
        }
        // Transform API object to data entities
        List<TDataEntity> entities =
            transformToDataEntities(result.result as TApiObject);
        // Update cache
        await _storeDataInCache(entities, cacheUpdateMode, filter);
        // Update cachedEntities with newly fetched data
        cachedEntities = await _getDataFromCache(filter);
      }

      // Transform data entities to models
      final models = cachedEntities.map(transformToModel).toList();
      return ResultOrError.fromResult(models);
    } catch (e) {
      return ResultOrError.fromError(ErrorInfo(-1)); // Replace with appropriate error code
    }
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    try {
      if (useMemoryCache) {
        final box = _memoryCache[boxKey];
        if (box != null) {
          final entity = box[key] as TDataEntity?;
          if (entity != null) {
            return transformToModel(entity);
          }
        }
      }
      // Get from database
      final entity = _database.getBeyKey(key);
      if (entity != null) {
        return transformToModel(entity);
      }
      return null;
    } catch (e) {
      return null;
    }
  }
}


  Google Gemini
final Map<String, Map<String, dynamic>> _memoryCache = {};

class ObjectDdCacheManager<TApiObject, TDataEntity extends DataEntity, TModel>
    extends CacheManager<TApiObject, TDataEntity, TModel> {
  ObjectDdCacheManager(
      {required super.fetchFromApi,
      required super.transformToDataEntities,
      required super.transformToModel,
      required super.boxName,
      super.useMemoryCache});

  ObjectDdMock<TDataEntity> get _db => ObjectDdMock<TDataEntity>();

    ObjectDdMock<TDataEntity> get _memory => ObjectDdMock<TDataEntity>();


  @override
  Future<ResultOrError<List<TModel>>> retrieveItems(
      {FetchDataPolicy fetchDataPolicy = FetchDataPolicy.onEmptyCache,
      CacheUpdateMode cacheUpdateMode = CacheUpdateMode.entities,
      Filter? filter}) async {
    List<TDataEntity> cachedEntities = [];

    if (useMemoryCache) {
      cachedEntities = _memory.getAll();
    }

    if (cachedEntities.isEmpty) {
      cachedEntities = _db.getAll();
    }

    if (fetchDataPolicy == FetchDataPolicy.never) {
      return ResultOrError.fromResult(
          cachedEntities.map(transformToModel).toList());
    }

    if (fetchDataPolicy == FetchDataPolicy.onEmptyCache &&
        cachedEntities.isNotEmpty) {
      return ResultOrError.fromResult(
          cachedEntities.map(transformToModel).toList());
    }

    final apiResult = await fetchFromApi();
    if (!apiResult.isSuccess) {
      return ResultOrError.fromError(apiResult.error!);
    }

    final newEntities = transformToDataEntities(apiResult.result!);

    if (cacheUpdateMode == CacheUpdateMode.box) {
      _db.deleteBox();
      if (useMemoryCache) {
        _memory.deleteBox();
      }
    } else if (filter != null) {
      List<TDataEntity> entitiesToDelete = [];
      if (filter.kind == FilterKind.entityKey) {
        final keyFilter = filter as FilterByKey;
        final entity = _db.getBeyKey(keyFilter.key);
        if (entity != null) {
          entitiesToDelete.add(entity);
        }
          if (useMemoryCache) {
            final memoryEntity = _memory.getBeyKey(keyFilter.key);
            if (memoryEntity != null) {
              entitiesToDelete.add(memoryEntity);
            }
          }
      } else if (filter.kind == FilterKind.range) {
        final rangeFilter = filter as FilterByRange;
        entitiesToDelete = _db.select(rangeFilter.startKey, rangeFilter.endKey);
        if (useMemoryCache) {
          entitiesToDelete.addAll(_memory.select(rangeFilter.startKey, rangeFilter.endKey));
        }
      }
      _db.deleteEntities(entitiesToDelete);
      if (useMemoryCache) {
        _memory.deleteEntities(entitiesToDelete);
      }
    }

    _db.putAll(newEntities);
        if (useMemoryCache) {
      _memory.putAll(newEntities);
    }

    return ResultOrError.fromResult(newEntities.map(transformToModel).toList());
  }

  @override
  Future<TModel?> getItemFromCache(String key) async {
    if (useMemoryCache) {
      final memoryEntity = _memory.getBeyKey(key);
      if (memoryEntity != null) {
        return transformToModel(memoryEntity);
      }
    }
    final entity = _db.getBeyKey(key);
    return entity != null ? transformToModel(entity) : null;
  }
}


Так же просьба, у кого есть возможность, позапускать на других версиях. Позже добавлю комменты.

З.Ы.
Так же добавляйте свое понимание — хорошее ли задание для ИИ, сколько бы у вас заняло времени.
Отредактировано 04.01.2025 22:17 Shmj . Предыдущая версия . Еще …
Отредактировано 04.01.2025 15:03 Shmj . Предыдущая версия .
Отредактировано 04.01.2025 14:03 Shmj . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.