SYMBOL INDEX (3220 symbols across 344 files) FILE: api/admin.py function deduplicate_faces_function (line 21) | def deduplicate_faces_function(queryset): class FaceAdmin (line 62) | class FaceAdmin(admin.ModelAdmin): class PhotoAdmin (line 76) | class PhotoAdmin(admin.ModelAdmin): method deduplicate_faces (line 88) | def deduplicate_faces(self, request, queryset): class ThumbnailAdmin (line 96) | class ThumbnailAdmin(admin.ModelAdmin): FILE: api/all_tasks.py function create_download_job (line 13) | def create_download_job(job_type, user, photos, filename): function zip_photos_task (line 26) | def zip_photos_task(job_id, user, photos, filename): function delete_zip_file (line 118) | def delete_zip_file(filename): FILE: api/api_util.py function get_current_job (line 13) | def get_current_job(): function shuffle (line 23) | def shuffle(list): function is_hidden (line 28) | def is_hidden(filepath): function has_hidden_attribute (line 33) | def has_hidden_attribute(filepath): function path_to_dict (line 40) | def path_to_dict(path, recurse=2): function get_search_term_examples (line 56) | def get_search_term_examples(user): FILE: api/apps.py class ApiConfig (line 4) | class ApiConfig(AppConfig): FILE: api/autoalbum.py function regenerate_event_titles (line 21) | def regenerate_event_titles(user, job_id): function generate_event_albums (line 46) | def generate_event_albums(user, job_id): function delete_missing_photos (line 157) | def delete_missing_photos(user, job_id): FILE: api/background_tasks.py function generate_captions (line 9) | def generate_captions(overwrite=False): function geolocate (line 26) | def geolocate(overwrite=False): function add_photos_to_album_things (line 41) | def add_photos_to_album_things(): FILE: api/batch_jobs.py function batch_calculate_clip_embedding (line 12) | def batch_calculate_clip_embedding(user): FILE: api/burst_detection_rules.py class BurstRuleTypes (line 26) | class BurstRuleTypes: class BurstRuleCategory (line 39) | class BurstRuleCategory: class BurstDetectionRule (line 66) | class BurstDetectionRule: method __init__ (line 85) | def __init__(self, params): method get_required_exif_tags (line 94) | def get_required_exif_tags(self): method _check_condition_path (line 115) | def _check_condition_path(self, path): method _check_condition_filename (line 122) | def _check_condition_filename(self, path): method _check_condition_exif (line 130) | def _check_condition_exif(self, exif_tags): method check_conditions (line 147) | def check_conditions(self, path, exif_tags): method is_burst_photo (line 155) | def is_burst_photo(self, photo, exif_tags): method _check_exif_burst_mode (line 185) | def _check_exif_burst_mode(self, photo, exif_tags): method _check_exif_sequence_number (line 211) | def _check_exif_sequence_number(self, photo, exif_tags): method _check_filename_pattern (line 233) | def _check_filename_pattern(self, photo, exif_tags): function check_filename_pattern (line 283) | def check_filename_pattern(photo, pattern_type="any"): function group_photos_by_timestamp (line 335) | def group_photos_by_timestamp(photos, interval_ms=2000, require_same_cam... function group_photos_by_visual_similarity (line 399) | def group_photos_by_visual_similarity(photos, similarity_threshold=15): function get_default_burst_detection_rules (line 541) | def get_default_burst_detection_rules(): function get_all_predefined_burst_rules (line 546) | def get_all_predefined_burst_rules(): function _as_json (line 551) | def _as_json(configs): function as_rules (line 561) | def as_rules(configs): function get_hard_rules (line 566) | def get_hard_rules(rules): function get_soft_rules (line 571) | def get_soft_rules(rules): function get_enabled_rules (line 576) | def get_enabled_rules(rules): FILE: api/cluster_manager.py class ClusterManager (line 10) | class ClusterManager: method try_add_cluster (line 12) | def try_add_cluster( FILE: api/date_time_extractor.py function _regexp_group_range (line 14) | def _regexp_group_range(a, b): function _extract_no_tz_datetime_from_str (line 64) | def _extract_no_tz_datetime_from_str(x, regexp=REGEXP_NO_TZ, group_mappi... class RuleTypes (line 112) | class RuleTypes: class TimeExtractionRule (line 119) | class TimeExtractionRule: method __init__ (line 224) | def __init__(self, params): method get_required_exif_tags (line 228) | def get_required_exif_tags(self): method _get_no_tz_dt_from_tag (line 237) | def _get_no_tz_dt_from_tag(self, tag_name, exif_tags): method _check_condition_path (line 244) | def _check_condition_path(self, path): method _check_condition_filename (line 250) | def _check_condition_filename(self, path): method _get_condition_exif (line 259) | def _get_condition_exif(self): method _check_condition_exif (line 271) | def _check_condition_exif(self, exif_tags): method _check_conditions (line 281) | def _check_conditions(self, path, exif_tags, gps_lat, gps_lon): method apply (line 288) | def apply( method _get_tz (line 304) | def _get_tz(self, description, gps_lat, gps_lon, user_default_tz): method _transform_tz (line 328) | def _transform_tz(self, dt, gps_lat, gps_lon, user_default_tz): method _apply_exif (line 349) | def _apply_exif(self, exif_tags, gps_lat, gps_lon, user_default_tz): method _apply_path (line 353) | def _apply_path(self, path, gps_lat, gps_lon, user_default_tz): method _apply_filesystem (line 374) | def _apply_filesystem(self, path, gps_lat, gps_lon, user_default_tz): function _check_gps_ok (line 385) | def _check_gps_ok(lat, lon): function set_as_default_rule (line 510) | def set_as_default_rule(rule): function set_as_other_rule (line 515) | def set_as_other_rule(rule): function _as_json (line 525) | def _as_json(configs): function as_rules (line 534) | def as_rules(configs): function extract_local_date_time (line 538) | def extract_local_date_time( FILE: api/directory_watcher/file_grouping.py function get_file_grouping_key (line 27) | def get_file_grouping_key(path: str) -> tuple[str, str]: function select_main_file (line 45) | def select_main_file(files: list[File]) -> File | None: function find_matching_jpeg_photo (line 64) | def find_matching_jpeg_photo(raw_path: str, user) -> Photo | None: function find_matching_image_for_video (line 97) | def find_matching_image_for_video(video_path: str, user) -> Photo | None: FILE: api/directory_watcher/file_handlers.py function create_file_record (line 31) | def create_file_record(user, path) -> File | None: function group_files_into_photo (line 64) | def group_files_into_photo(user, files: list[File], job_id) -> Photo | N... function create_new_image (line 150) | def create_new_image(user, path) -> Photo | None: function handle_new_image (line 248) | def handle_new_image(user, path, job_id, photo=None): function handle_file_group (line 281) | def handle_file_group(user, file_paths: list[str], job_id): function _process_photo (line 332) | def _process_photo(photo: Photo, path: str, job_id, start: datetime.date... FILE: api/directory_watcher/processing_jobs.py function generate_face_embeddings (line 23) | def generate_face_embeddings(user, job_id: UUID): function generate_tags (line 66) | def generate_tags(user, job_id: UUID, full_scan=False): function generate_tag_job (line 120) | def generate_tag_job(photo: Photo, job_id: str): function add_geolocation (line 143) | def add_geolocation(user, job_id: UUID, full_scan=False): function geolocation_job (line 185) | def geolocation_job(photo: Photo, job_id: UUID): function scan_faces (line 207) | def scan_faces(user, job_id: UUID, full_scan=False): FILE: api/directory_watcher/repair_jobs.py function repair_ungrouped_file_variants (line 16) | def repair_ungrouped_file_variants(user, job_id: UUID): FILE: api/directory_watcher/scan_jobs.py function _file_was_modified_after (line 43) | def _file_was_modified_after(filepath, time): function wait_for_group_and_process_metadata (line 52) | def wait_for_group_and_process_metadata( function photo_scanner (line 150) | def photo_scanner(user, last_scan, full_scan, path, job_id): function scan_photos (line 173) | def scan_photos(user, full_scan, job_id, scan_directory="", scan_files=[]): function scan_missing_photos (line 374) | def scan_missing_photos(user, job_id: UUID): FILE: api/directory_watcher/utils.py function should_skip (line 15) | def should_skip(path): function is_hidden (line 30) | def is_hidden(path): function _has_hidden_attribute (line 35) | def _has_hidden_attribute(path): function is_hidden (line 44) | def is_hidden(path): function walk_directory (line 49) | def walk_directory(directory, callback): function walk_files (line 66) | def walk_files(scan_files, callback): function update_scan_counter (line 79) | def update_scan_counter(job_id, failed=False, error=None): FILE: api/drf_optimize.py class OptimizeRelatedModelViewSetMetaclass (line 8) | class OptimizeRelatedModelViewSetMetaclass(type): method get_many_to_many_rel (line 21) | def get_many_to_many_rel(cls, info, meta_fields): method get_lookups (line 35) | def get_lookups(cls, fields, strict=False): method get_many_to_one_rel (line 42) | def get_many_to_one_rel(cls, info, meta_fields): method get_forward_rel (line 61) | def get_forward_rel(cls, info, meta_fields): method __new__ (line 68) | def __new__(cls, name, bases, attrs): FILE: api/duplicate_detection.py class BKTree (line 37) | class BKTree: method __init__ (line 44) | def __init__(self, distance_func): method add (line 49) | def add(self, item_id, item_hash): method search (line 69) | def search(self, query_hash, threshold): class UnionFind (line 94) | class UnionFind: method __init__ (line 97) | def __init__(self): method find (line 101) | def find(self, x): method union (line 110) | def union(self, x, y): method get_groups (line 120) | def get_groups(self): function detect_exact_copies (line 127) | def detect_exact_copies(user, progress_callback=None): function detect_visual_duplicates (line 274) | def detect_visual_duplicates( function batch_detect_duplicates (line 452) | def batch_detect_duplicates(user, options=None): FILE: api/face_classify.py function cluster_faces (line 31) | def cluster_faces(user, inferred=True): function cluster_all_faces (line 83) | def cluster_all_faces(user, job_id) -> bool: function create_all_clusters (line 117) | def create_all_clusters(user: User, lrj: LongRunningJob = None) -> int: function delete_persons_without_faces (line 216) | def delete_persons_without_faces(): function delete_clusters (line 222) | def delete_clusters(user: User): function delete_clustered_people (line 230) | def delete_clustered_people(user: User): function filter_data (line 240) | def filter_data(encodings, ids): function train_faces (line 259) | def train_faces(user: User, job_id) -> bool: FILE: api/face_extractor.py class RuleTypes (line 10) | class RuleTypes: function extract_from_exif (line 15) | def extract_from_exif(image_path, big_thumbnail_image_path): function extract_from_dlib (line 97) | def extract_from_dlib(image_path, big_thumbnail_path, owner): function extract (line 113) | def extract(image_path, big_thumbnail_path, owner): FILE: api/face_recognition.py function get_face_encodings (line 5) | def get_face_encodings(image_path, known_face_locations): function get_face_locations (line 20) | def get_face_locations(image_path, model="hog"): FILE: api/feature/embedded_media.py function _locate_embedded_video_google (line 16) | def _locate_embedded_video_google(data): function _locate_embedded_video_samsung (line 25) | def _locate_embedded_video_samsung(data): function has_embedded_media (line 32) | def has_embedded_media(path: str) -> bool: function extract_embedded_media (line 45) | def extract_embedded_media(path: str, hash: str) -> str | None: FILE: api/feature/tests/test_embedded_media.py function create_test_file (line 17) | def create_test_file(path: str, user: User, content: bytes): class EmbeddedMediaTest (line 32) | class EmbeddedMediaTest(APITestCase): method setUp (line 33) | def setUp(self): method test_should_not_process_non_jpeg_files (line 39) | def test_should_not_process_non_jpeg_files(self): method test_google_pixel_motion_photo_signatures (line 44) | def test_google_pixel_motion_photo_signatures(self): method test_samsung_motion_photo_signature (line 51) | def test_samsung_motion_photo_signature(self): method test_other_content_should_not_report_as_having_embedded_media (line 57) | def test_other_content_should_not_report_as_having_embedded_media(self): method test_extract_embedded_media_from_google_motion_photo (line 62) | def test_extract_embedded_media_from_google_motion_photo(self): method test_extract_embedded_media_from_samsung_motion_photo (line 73) | def test_extract_embedded_media_from_samsung_motion_photo(self): method test_fetch_embedded_media_as_owner (line 83) | def test_fetch_embedded_media_as_owner(self): method test_fetch_embedded_media_as_anonymous_when_photo_is_public (line 91) | def test_fetch_embedded_media_as_anonymous_when_photo_is_public(self): method test_fetch_embedded_media_as_anonymous_when_photo_is_private (line 100) | def test_fetch_embedded_media_as_anonymous_when_photo_is_private(self): method test_fetch_embedded_media_when_photo_does_not_have_embedded_media (line 109) | def test_fetch_embedded_media_when_photo_does_not_have_embedded_media(... FILE: api/filters.py class SemanticSearchFilter (line 13) | class SemanticSearchFilter(filters.SearchFilter): method filter_queryset (line 14) | def filter_queryset(self, request, queryset, view): FILE: api/geocode/config.py function _get_config (line 9) | def _get_config(): function get_provider_config (line 36) | def get_provider_config(provider) -> dict: function get_provider_parser (line 43) | def get_provider_parser(provider) -> callable: FILE: api/geocode/geocode.py class Geocode (line 11) | class Geocode: method __init__ (line 12) | def __init__(self, provider): method reverse (line 19) | def reverse(self, lat: float, lon: float) -> dict: method search (line 32) | def search(self, query: str, limit: int = 5) -> List[dict]: function reverse_geocode (line 55) | def reverse_geocode(lat: float, lon: float) -> dict: function search_location (line 63) | def search_location(query: str, limit: int = 5) -> List[dict]: FILE: api/geocode/parsers/mapbox.py function parse (line 4) | def parse(location): FILE: api/geocode/parsers/nominatim.py function parse (line 4) | def parse(location): FILE: api/geocode/parsers/opencage.py function parse (line 4) | def parse(location): FILE: api/geocode/parsers/tomtom.py function _dedup (line 6) | def _dedup(iterable): function parse (line 18) | def parse(location): FILE: api/image_captioning.py function generate_caption (line 5) | def generate_caption(image_path, blip=False, prompt=None): function unload_model (line 53) | def unload_model(): FILE: api/image_similarity.py function search_similar_embedding (line 13) | def search_similar_embedding(user, emb, result_count=100, threshold=27): function search_similar_image (line 35) | def search_similar_image(user, photo, threshold=27): function build_image_similarity_index (line 62) | def build_image_similarity_index(user): FILE: api/llm.py function image_to_base64_data_uri (line 8) | def image_to_base64_data_uri(image_path): function generate_prompt (line 31) | def generate_prompt(prompt, image_path=None): FILE: api/management/commands/build_similarity_index.py class Command (line 8) | class Command(BaseCommand): method handle (line 11) | def handle(self, *args, **kwargs): FILE: api/management/commands/clear_cache.py class Command (line 6) | class Command(BaseCommand): method handle (line 11) | def handle(self, *args, **kwargs): FILE: api/management/commands/createadmin.py class Command (line 10) | class Command(BaseCommand): method add_arguments (line 13) | def add_arguments(self, parser): method handle (line 33) | def handle(self, *args, **options): FILE: api/management/commands/createuser.py class Command (line 10) | class Command(BaseCommand): method add_arguments (line 13) | def add_arguments(self, parser): method handle (line 42) | def handle(self, *args, **options): FILE: api/management/commands/save_metadata.py class Command (line 7) | class Command(BaseCommand): method add_arguments (line 10) | def add_arguments(self, parser): method handle (line 40) | def handle(self, *args, **options): FILE: api/management/commands/scan.py class Command (line 12) | class Command(BaseCommand): method add_arguments (line 15) | def add_arguments(self, parser): method handle (line 30) | def handle(self, *args, **options): method nextcloud_scan (line 59) | def nextcloud_scan(self): FILE: api/management/commands/start_cleaning_service.py class Command (line 8) | class Command(BaseCommand): method handle (line 11) | def handle(self, *args, **kwargs): FILE: api/management/commands/start_job_cleanup_service.py class Command (line 8) | class Command(BaseCommand): method handle (line 11) | def handle(self, *args, **kwargs): FILE: api/management/commands/start_service.py class Command (line 8) | class Command(BaseCommand): method add_arguments (line 12) | def add_arguments(self, parser): method handle (line 23) | def handle(self, *args, **kwargs): FILE: api/metadata/face_regions.py function thumbnail_coords_to_normalized (line 10) | def thumbnail_coords_to_normalized(top, right, bottom, left, thumb_width... function reverse_orientation_transform (line 20) | def reverse_orientation_transform(x, y, w, h, orientation): function _escape_exiftool_value (line 70) | def _escape_exiftool_value(value): function build_face_region_exiftool_args (line 82) | def build_face_region_exiftool_args(face_regions, image_width=None, imag... function get_face_region_tags (line 127) | def get_face_region_tags(photo): FILE: api/metadata/reader.py function get_sidecar_files_in_priority_order (line 7) | def get_sidecar_files_in_priority_order(media_file): function _get_existing_metadata_files_reversed (line 21) | def _get_existing_metadata_files_reversed(media_file, include_sidecar_fi... function get_metadata (line 33) | def get_metadata(media_file, tags, try_sidecar=True, struct=False): FILE: api/metadata/tags.py class Tags (line 1) | class Tags: FILE: api/metadata/writer.py function write_metadata (line 9) | def write_metadata(media_file, tags, use_sidecar=True): FILE: api/middleware.py class FingerPrintMiddleware (line 1) | class FingerPrintMiddleware: method __init__ (line 2) | def __init__(self, get_response): method __call__ (line 6) | def __call__(self, request): FILE: api/migrations/0001_initial.py class Migration (line 16) | class Migration(migrations.Migration): FILE: api/migrations/0002_add_confidence.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0003_remove_unused_thumbs.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0004_fix_album_thing_constraint.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0005_add_video_to_photo.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0006_migrate_to_boolean_field.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0007_migrate_to_json_field.py class Migration (line 6) | class Migration(migrations.Migration): method forwards_func (line 11) | def forwards_func(apps, schema): FILE: api/migrations/0008_remove_image_path.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0009_add_aspect_ratio.py class Migration (line 5) | class Migration(migrations.Migration): method forwards_func (line 10) | def forwards_func(apps, schema): FILE: api/migrations/0009_add_clip_embedding_field.py class Migration (line 5) | class Migration(migrations.Migration): FILE: api/migrations/0010_merge_20210725_1547.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0011_a_add_rating.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0011_b_migrate_favorited_to_rating.py function favorited_to_rating (line 6) | def favorited_to_rating(apps, schema_editor): function rating_to_favorited (line 13) | def rating_to_favorited(apps, schema_editor): class Migration (line 20) | class Migration(migrations.Migration): FILE: api/migrations/0011_c_remove_favorited.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0012_add_favorite_min_rating.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0013_add_image_scale_and_misc.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0014_add_save_metadata_to_disk.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0015_add_dominant_color.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0016_add_transcode_videos.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0017_add_cover_photo.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0018_user_config_datetime_rules.py class Migration (line 8) | class Migration(migrations.Migration): FILE: api/migrations/0019_change_config_datetime_rules.py class Migration (line 8) | class Migration(migrations.Migration): FILE: api/migrations/0020_add_default_timezone.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0021_remove_photo_image.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0022_photo_video_length.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0023_photo_deleted.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0024_photo_timestamp.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0025_add_cover_photo.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0026_add_cluster_info.py class Migration (line 11) | class Migration(migrations.Migration): FILE: api/migrations/0027_rename_unknown_person.py function migrate_unknown (line 8) | def migrate_unknown(apps, schema_editor): function unmigrate_unknown (line 25) | def unmigrate_unknown(apps, schema_editor): class Migration (line 36) | class Migration(migrations.Migration): FILE: api/migrations/0028_add_metadata_fields.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0029_change_to_text_field.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0030_user_confidence_person.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0031_remove_account.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0032_always_have_owner.py function add_cluster_owner (line 6) | def add_cluster_owner(apps, schema_editor): function remove_cluster_owner (line 14) | def remove_cluster_owner(apps, schema_editor): class Migration (line 20) | class Migration(migrations.Migration): FILE: api/migrations/0033_add_post_delete_person.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0034_allow_deleting_person.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0035_add_files_model.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0036_handle_missing_files.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0037_migrate_to_files.py function find_out_type (line 14) | def find_out_type(path): function migrate_to_files (line 24) | def migrate_to_files(apps, schema_editor): function remove_files (line 56) | def remove_files(apps, schema_editor): class Migration (line 62) | class Migration(migrations.Migration): FILE: api/migrations/0038_add_main_file.py function find_out_type (line 12) | def find_out_type(path): function add_main_file (line 22) | def add_main_file(apps, schema_editor): function remove_main_file (line 30) | def remove_main_file(apps, schema_editor): class Migration (line 37) | class Migration(migrations.Migration): FILE: api/migrations/0039_remove_photo_image_paths.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0040_add_user_public_sharing_flag.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0041_apply_user_enum_for_person.py class Migration (line 4) | class Migration(migrations.Migration): method apply_enum (line 5) | def apply_enum(apps, schema_editor): method remove_enum (line 11) | def remove_enum(apps, schema_editor): FILE: api/migrations/0042_alter_albumuser_cover_photo_alter_photo_main_file.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0043_alter_photo_size.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0044_alter_cluster_person_alter_person_cluster_owner.py class Migration (line 8) | class Migration(migrations.Migration): FILE: api/migrations/0045_alter_face_cluster.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0046_add_embedded_media.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0047_alter_file_embedded_media.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0048_fix_null_height.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0049_fix_metadata_files_as_main_files.py function delete_photos_with_metadata_as_main (line 4) | def delete_photos_with_metadata_as_main(apps, schema_editor): class Migration (line 10) | class Migration(migrations.Migration): FILE: api/migrations/0050_person_face_count.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0051_set_person_defaults.py class Migration (line 4) | class Migration(migrations.Migration): method apply_default (line 5) | def apply_default(apps, schema_editor): method remove_default (line 38) | def remove_default(apps, schema_editor): FILE: api/migrations/0052_alter_person_name.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0053_user_confidence_unknown_face_and_more.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0054_user_cluster_selection_epsilon_user_min_samples.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0055_alter_longrunningjob_job_type.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0056_user_llm_settings_alter_longrunningjob_job_type.py class Migration (line 8) | class Migration(migrations.Migration): FILE: api/migrations/0057_remove_face_image_path_and_more.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0058_alter_user_avatar_alter_user_nextcloud_app_password_and_more.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0059_person_cover_face.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0060_apply_default_face_cover.py class Migration (line 4) | class Migration(migrations.Migration): method apply_default (line 5) | def apply_default(apps, schema_editor): method remove_default (line 22) | def remove_default(apps, schema_editor): FILE: api/migrations/0061_alter_person_name.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0062_albumthing_cover_photos.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0063_apply_default_album_things_cover.py class Migration (line 4) | class Migration(migrations.Migration): method apply_default (line 5) | def apply_default(apps, schema_editor): method remove_default (line 13) | def remove_default(apps, schema_editor): FILE: api/migrations/0064_albumthing_photo_count.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0065_apply_default_photo_count.py class Migration (line 4) | class Migration(migrations.Migration): method apply_default (line 5) | def apply_default(apps, schema_editor): method remove_default (line 12) | def remove_default(apps, schema_editor): FILE: api/migrations/0066_photo_last_modified_alter_longrunningjob_job_type.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0067_alter_longrunningjob_job_type.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0068_remove_longrunningjob_result_and_more.py function copy_progress_data (line 5) | def copy_progress_data(apps, schema_editor): class Migration (line 14) | class Migration(migrations.Migration): FILE: api/migrations/0069_rename_to_in_trashcan.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/migrations/0070_photo_removed.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0071_rename_person_label_probability_face_cluster_probability_and_more.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0072_alter_face_person.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0073_remove_unknown_person.py function delete_unknown_person_and_update_faces (line 4) | def delete_unknown_person_and_update_faces(apps, schema_editor): function recreate_unknown_person_and_restore_faces (line 24) | def recreate_unknown_person_and_restore_faces(apps, schema_editor): class Migration (line 48) | class Migration(migrations.Migration): FILE: api/migrations/0074_migrate_cluster_person.py function move_person_to_cluster_if_kind_cluster (line 4) | def move_person_to_cluster_if_kind_cluster(apps, schema_editor): function restore_person_from_cluster (line 24) | def restore_person_from_cluster(apps, schema_editor): class Migration (line 38) | class Migration(migrations.Migration): FILE: api/migrations/0075_alter_face_cluster_person.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0076_alter_file_path_alter_longrunningjob_job_type_and_more.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0077_alter_albumdate_title.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0078_create_photo_thumbnail.py class Migration (line 5) | class Migration(migrations.Migration): FILE: api/migrations/0079_alter_albumauto_title.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0080_create_photo_caption.py function migrate_caption_data (line 7) | def migrate_caption_data(apps, schema_editor): function reverse_migrate_caption_data (line 40) | def reverse_migrate_caption_data(apps, schema_editor): class Migration (line 58) | class Migration(migrations.Migration): FILE: api/migrations/0081_remove_caption_fields_from_photo.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0082_create_photo_search.py function migrate_search_data (line 7) | def migrate_search_data(apps, schema_editor): function reverse_migrate_search_data (line 45) | def reverse_migrate_search_data(apps, schema_editor): class Migration (line 68) | class Migration(migrations.Migration): FILE: api/migrations/0083_remove_search_fields.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0084_convert_arrayfield_to_json.py function copy_arrayfield_to_json (line 5) | def copy_arrayfield_to_json(apps, schema_editor): function copy_json_to_arrayfield (line 19) | def copy_json_to_arrayfield(apps, schema_editor): class Migration (line 31) | class Migration(migrations.Migration): FILE: api/migrations/0085_albumuser_public_expires_at_albumuser_public_slug.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0086_remove_albumuser_public_and_more.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0087_add_folder_album.py class Migration (line 8) | class Migration(migrations.Migration): FILE: api/migrations/0088_remove_folder_album.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0089_add_text_alignment.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0090_add_header_size.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0091_alter_user_scan_directory.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0092_add_skip_raw_files_field.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0093_migrate_photon_to_nominatim.py function migrate_photon_to_nominatim (line 11) | def migrate_photon_to_nominatim(apps, schema_editor): function reverse_migration (line 24) | def reverse_migration(apps, schema_editor): class Migration (line 36) | class Migration(migrations.Migration): FILE: api/migrations/0094_add_slideshow_interval.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0095_photo_perceptual_hash_alter_longrunningjob_job_type_and_more.py class Migration (line 9) | class Migration(migrations.Migration): FILE: api/migrations/0096_add_progress_step_and_result_to_longrunningjob.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0097_add_duplicate_detection_settings_to_user.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0098_add_photo_stack.py function get_deleted_user (line 8) | def get_deleted_user(): class Migration (line 14) | class Migration(migrations.Migration): FILE: api/migrations/0099_photo_uuid_primary_key.py function migrate_forward (line 268) | def migrate_forward(apps, schema_editor): function migrate_reverse (line 282) | def migrate_reverse(apps, schema_editor): function _migrate_postgresql (line 295) | def _migrate_postgresql(schema_editor): function _migrate_sqlite (line 320) | def _migrate_sqlite(schema_editor): function _sqlite_recreate_table (line 381) | def _sqlite_recreate_table(cursor, table_name, pk_column, column_overrid... function _sqlite_update_fk_table (line 431) | def _sqlite_update_fk_table(cursor, table_name, fk_column, mapping): function _sqlite_create_indexes (line 504) | def _sqlite_create_indexes(cursor): function _sqlite_column_info (line 526) | def _sqlite_column_info(cursor, table_name): function _sqlite_index_info (line 532) | def _sqlite_index_info(cursor, table_name): class Migration (line 546) | class Migration(migrations.Migration): FILE: api/migrations/0100_metadataedit_metadatafile_photometadata_stackreview_and_more.py class Migration (line 10) | class Migration(migrations.Migration): FILE: api/migrations/0101_populate_photo_metadata.py function populate_photo_metadata (line 10) | def populate_photo_metadata(apps, schema_editor): function reverse_populate (line 103) | def reverse_populate(apps, schema_editor): class Migration (line 112) | class Migration(migrations.Migration): FILE: api/migrations/0102_photo_stacks_manytomany.py function migrate_fk_to_m2m (line 14) | def migrate_fk_to_m2m(apps, schema_editor): function reverse_m2m_to_fk (line 31) | def reverse_m2m_to_fk(apps, schema_editor): class Migration (line 47) | class Migration(migrations.Migration): FILE: api/migrations/0103_remove_photo_metadata_fields.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0104_remove_photostack_potential_savings_and_more.py class Migration (line 10) | class Migration(migrations.Migration): FILE: api/migrations/0105_alter_photo_image_hash.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0106_alter_longrunningjob_options.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0107_add_burst_detection_rules.py class Migration (line 8) | class Migration(migrations.Migration): FILE: api/migrations/0108_add_stack_raw_jpeg_field.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0109_migrate_skip_raw_to_stack_raw_jpeg.py function migrate_skip_raw_to_stack_raw_jpeg (line 8) | def migrate_skip_raw_to_stack_raw_jpeg(apps, schema_editor): function reverse_migration (line 17) | def reverse_migration(apps, schema_editor): class Migration (line 24) | class Migration(migrations.Migration): FILE: api/migrations/0110_fix_file_embedded_media_self_reference.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0111_alter_file_embedded_media.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0112_convert_file_stacks_to_variants.py function convert_raw_jpeg_stacks_to_file_variants (line 36) | def convert_raw_jpeg_stacks_to_file_variants(apps, schema_editor): function _flush_raw_jpeg_batch (line 150) | def _flush_raw_jpeg_batch(PhotoFiles, PhotoStacks, Photo, PhotoStack, function convert_live_photo_stacks_to_file_variants (line 172) | def convert_live_photo_stacks_to_file_variants(apps, schema_editor): function _flush_live_photo_batch (line 288) | def _flush_live_photo_batch(PhotoFiles, PhotoStacks, Photo, PhotoStack, function forward_migration (line 310) | def forward_migration(apps, schema_editor): function reverse_migration (line 316) | def reverse_migration(apps, schema_editor): class Migration (line 327) | class Migration(migrations.Migration): FILE: api/migrations/0113_alter_photostack_stack_type.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0114_add_file_path_unique.py function deduplicate_file_paths (line 11) | def deduplicate_file_paths(apps, schema_editor): function reverse_deduplicate (line 152) | def reverse_deduplicate(apps, schema_editor): class Migration (line 160) | class Migration(migrations.Migration): FILE: api/migrations/0115_cleanup_duplicate_photos.py function cleanup_duplicate_photos (line 11) | def cleanup_duplicate_photos(apps, schema_editor): function reverse_cleanup (line 196) | def reverse_cleanup(apps, schema_editor): class Migration (line 203) | class Migration(migrations.Migration): FILE: api/migrations/0116_cleanup_duplicate_groups_removed_photos.py function cleanup_duplicate_groups (line 7) | def cleanup_duplicate_groups(apps, schema_editor): function reverse_cleanup (line 44) | def reverse_cleanup(apps, schema_editor): class Migration (line 53) | class Migration(migrations.Migration): FILE: api/migrations/0117_delete_removed_photos.py function delete_removed_photos (line 7) | def delete_removed_photos(apps, schema_editor): function reverse_delete (line 32) | def reverse_delete(apps, schema_editor): class Migration (line 39) | class Migration(migrations.Migration): FILE: api/migrations/0118_alter_longrunningjob_job_type.py class Migration (line 6) | class Migration(migrations.Migration): FILE: api/migrations/0119_add_public_sharing_options.py class Migration (line 7) | class Migration(migrations.Migration): FILE: api/migrations/0120_rename_thumbnails_uuid_to_hash.py function rename_thumbnails_uuid_to_hash (line 10) | def rename_thumbnails_uuid_to_hash(apps, schema_editor): function reverse_rename_thumbnails (line 103) | def reverse_rename_thumbnails(apps, schema_editor): class Migration (line 114) | class Migration(migrations.Migration): FILE: api/migrations/0121_add_default_tagging_model.py function add_default_tagging_model (line 13) | def add_default_tagging_model(apps, schema_editor): function reverse_migration (line 24) | def reverse_migration(apps, schema_editor): class Migration (line 33) | class Migration(migrations.Migration): FILE: api/migrations/0121_user_save_face_tags_to_disk.py class Migration (line 4) | class Migration(migrations.Migration): FILE: api/ml_models.py class MlTypes (line 14) | class MlTypes: function download_model (line 109) | def download_model(model): function _download_file (line 204) | def _download_file(url, target_path, model_name): function download_models (line 235) | def download_models(user): function do_all_models_exist (line 253) | def do_all_models_exist(): FILE: api/models/album_auto.py class AlbumAuto (line 11) | class AlbumAuto(models.Model): class Meta (line 27) | class Meta: method _generate_title (line 30) | def _generate_title(self): method __str__ (line 111) | def __str__(self): FILE: api/models/album_date.py class AlbumDate (line 7) | class AlbumDate(models.Model): class Meta (line 19) | class Meta: method __str__ (line 22) | def __str__(self): method ordered_photos (line 25) | def ordered_photos(self): function get_or_create_album_date (line 29) | def get_or_create_album_date(date, owner): function get_album_date (line 36) | def get_album_date(date, owner): function get_album_nodate (line 43) | def get_album_nodate(owner): FILE: api/models/album_place.py class AlbumPlace (line 7) | class AlbumPlace(models.Model): class Meta (line 18) | class Meta: method __str__ (line 21) | def __str__(self): function get_album_place (line 25) | def get_album_place(title, owner): FILE: api/models/album_thing.py function update_default_cover_photo (line 9) | def update_default_cover_photo(instance): class AlbumThing (line 17) | class AlbumThing(models.Model): class Meta (line 31) | class Meta: method save (line 38) | def save(self, *args, **kwargs): method update_default_cover_photo (line 41) | def update_default_cover_photo(self): method __str__ (line 44) | def __str__(self): function update_photo_count (line 49) | def update_photo_count(sender, instance, action, reverse, model, pk_set,... function get_album_thing (line 57) | def get_album_thing(title, owner, thing_type=None): FILE: api/models/album_user.py class AlbumUser (line 7) | class AlbumUser(models.Model): method __str__ (line 25) | def __str__(self): class Meta (line 28) | class Meta: FILE: api/models/album_user_share.py class AlbumUserShare (line 8) | class AlbumUserShare(models.Model): method ensure_slug (line 25) | def ensure_slug(self) -> None: method is_active (line 39) | def is_active(self) -> bool: method save (line 46) | def save(self, *args, **kwargs): method get_effective_sharing_settings (line 51) | def get_effective_sharing_settings(self) -> dict: FILE: api/models/cluster.py class Cluster (line 13) | class Cluster(models.Model): method __str__ (line 29) | def __str__(self): method get_mean_encoding_array (line 32) | def get_mean_encoding_array(self) -> np.ndarray: method set_metadata (line 35) | def set_metadata(self, all_vectors): method get_or_create_cluster_by_name (line 41) | def get_or_create_cluster_by_name(user: User, name): method get_or_create_cluster_by_id (line 45) | def get_or_create_cluster_by_id(user: User, cluster_id: int): method calculate_mean_face_encoding (line 55) | def calculate_mean_face_encoding(all_encodings): function get_unknown_cluster (line 59) | def get_unknown_cluster(user: User) -> Cluster: FILE: api/models/duplicate.py class Duplicate (line 26) | class Duplicate(models.Model): class DuplicateType (line 34) | class DuplicateType(models.TextChoices): class ReviewStatus (line 40) | class ReviewStatus(models.TextChoices): class Meta (line 96) | class Meta: method __str__ (line 105) | def __str__(self): method photo_count (line 109) | def photo_count(self): method get_photos_ordered_by_quality (line 113) | def get_photos_ordered_by_quality(self): method auto_select_best_photo (line 122) | def auto_select_best_photo(self): method calculate_potential_savings (line 151) | def calculate_potential_savings(self): method resolve (line 172) | def resolve(self, kept_photo, trash_others: bool = True): method dismiss (line 193) | def dismiss(self): method revert (line 205) | def revert(self): method merge_with (line 224) | def merge_with(self, other_duplicate: "Duplicate"): method create_or_merge (line 244) | def create_or_merge(cls, owner, duplicate_type, photos, similarity_sco... FILE: api/models/face.py class Face (line 13) | class Face(models.Model): method timestamp (line 58) | def timestamp(self): method __str__ (line 61) | def __str__(self): method generate_encoding (line 64) | def generate_encoding(self): method get_encoding_array (line 82) | def get_encoding_array(self): function reset_person (line 87) | def reset_person(sender, instance, **kwargs): function auto_delete_file_on_delete (line 93) | def auto_delete_file_on_delete(sender, instance, **kwargs): FILE: api/models/file.py class File (line 16) | class File(models.Model): method __str__ (line 40) | def __str__(self): method create (line 44) | def create(path: str, user): method _find_out_type (line 95) | def _find_out_type(self): function is_video (line 106) | def is_video(path): function is_raw (line 116) | def is_raw(path): function is_metadata (line 160) | def is_metadata(path): function is_valid_media (line 168) | def is_valid_media(path, user) -> bool: function calculate_hash (line 181) | def calculate_hash(user, path): function calculate_hash_b64 (line 193) | def calculate_hash_b64(user, content): FILE: api/models/long_running_job.py class LongRunningJob (line 10) | class LongRunningJob(models.Model): class Meta (line 66) | class Meta: method __str__ (line 71) | def __str__(self): method is_running (line 76) | def is_running(self): method duration (line 81) | def duration(self): method start (line 88) | def start(self): method complete (line 93) | def complete(self, result=None): method fail (line 101) | def fail(self, error=None): method update_progress (line 110) | def update_progress(self, current, target=None, step=None): method set_result (line 122) | def set_result(self, result): method create_job (line 128) | def create_job(cls, user, job_type, job_id=None, start_now=False): method get_or_create_job (line 154) | def get_or_create_job(cls, user, job_type, job_id): method cleanup_stuck_jobs (line 176) | def cleanup_stuck_jobs(cls, hours=24): method cleanup_old_jobs (line 205) | def cleanup_old_jobs(cls, days=30): FILE: api/models/person.py class Person (line 14) | class Person(models.Model): method __str__ (line 47) | def __str__(self): method _calculate_face_count (line 61) | def _calculate_face_count(self): method _set_default_cover_photo (line 69) | def _set_default_cover_photo(self): method get_photos (line 75) | def get_photos(self, owner): function get_unknown_person (line 105) | def get_unknown_person(owner: User = None): function get_or_create_person (line 115) | def get_or_create_person(name, owner: User = None, kind: str = Person.KI... FILE: api/models/photo.py class VisiblePhotoManager (line 27) | class VisiblePhotoManager(models.Manager): method get_queryset (line 28) | def get_queryset(self): class Photo (line 41) | class Photo(models.Model): method get_clip_embeddings (line 127) | def get_clip_embeddings(self): method set_clip_embeddings (line 143) | def set_clip_embeddings(self, embeddings): method from_db (line 148) | def from_db(cls, db, field_names, values): method save (line 157) | def save( method _save_metadata (line 183) | def _save_metadata( method _find_album_place (line 216) | def _find_album_place(self): method _find_album_date (line 221) | def _find_album_date(self): method _extract_date_time_from_exif (line 247) | def _extract_date_time_from_exif(self, commit=True): method _geolocate (line 287) | def _geolocate(self, commit=True): method _add_location_to_album_dates (line 353) | def _add_location_to_album_dates(self): method _extract_faces (line 374) | def _extract_faces(self, second_try=False): method _add_to_album_thing (line 455) | def _add_to_album_thing(self): method _check_files (line 489) | def _check_files(self): method manual_delete (line 497) | def manual_delete(self): method _set_embedded_media (line 566) | def _set_embedded_media(self, obj): method __str__ (line 569) | def __str__(self): FILE: api/models/photo_caption.py class PhotoCaption (line 11) | class PhotoCaption(models.Model): class Meta (line 25) | class Meta: method __str__ (line 28) | def __str__(self): method generate_captions_im2txt (line 31) | def generate_captions_im2txt(self, commit=True): method _generate_captions_moondream (line 115) | def _generate_captions_moondream(self, commit=True): method save_user_caption (line 197) | def save_user_caption(self, caption, commit=True): method recreate_search_captions (line 261) | def recreate_search_captions(self): method generate_tag_captions (line 269) | def generate_tag_captions(self, commit=True): method _update_places365_album_things (line 330) | def _update_places365_album_things(self, res_places365): method _update_siglip2_album_things (line 364) | def _update_siglip2_album_things(self, siglip2_result): method generate_places365_captions (line 387) | def generate_places365_captions(self, commit=True): FILE: api/models/photo_metadata.py class MetadataFile (line 27) | class MetadataFile(models.Model): class FileType (line 38) | class FileType(models.TextChoices): class Source (line 44) | class Source(models.TextChoices): class Meta (line 92) | class Meta: method __str__ (line 97) | def __str__(self): class PhotoMetadata (line 101) | class PhotoMetadata(models.Model): class Source (line 115) | class Source(models.TextChoices): class Meta (line 238) | class Meta: method __str__ (line 247) | def __str__(self): method resolution (line 251) | def resolution(self): method megapixels (line 258) | def megapixels(self): method has_location (line 265) | def has_location(self): method camera_display (line 270) | def camera_display(self): method lens_display (line 280) | def lens_display(self): method extract_exif_data (line 289) | def extract_exif_data(cls, photo, commit=True): class MetadataEdit (line 403) | class MetadataEdit(models.Model): class Meta (line 438) | class Meta: method __str__ (line 446) | def __str__(self): FILE: api/models/photo_search.py class PhotoSearch (line 7) | class PhotoSearch(models.Model): class Meta (line 22) | class Meta: method __str__ (line 25) | def __str__(self): method recreate_search_captions (line 28) | def recreate_search_captions(self): method update_search_location (line 99) | def update_search_location(self, geolocation_json): FILE: api/models/photo_stack.py class PhotoStack (line 29) | class PhotoStack(models.Model): class StackType (line 41) | class StackType(models.TextChoices): class Meta (line 101) | class Meta: method __str__ (line 109) | def __str__(self): method photo_count (line 113) | def photo_count(self): method get_photos_ordered_by_quality (line 117) | def get_photos_ordered_by_quality(self): method auto_select_primary (line 124) | def auto_select_primary(self): method merge_with (line 172) | def merge_with(self, other_stack: "PhotoStack"): method create_or_merge (line 196) | def create_or_merge(cls, owner, stack_type, photos, sequence_start=Non... FILE: api/models/stack_review.py class StackReview (line 23) | class StackReview(models.Model): class Decision (line 39) | class Decision(models.TextChoices): class Meta (line 87) | class Meta: method __str__ (line 95) | def __str__(self): method is_reviewable_type (line 99) | def is_reviewable_type(cls, stack_type: str) -> bool: method create_for_stack (line 116) | def create_for_stack(cls, stack: PhotoStack) -> "StackReview | None": method resolve (line 133) | def resolve(self, kept_photo, trash_others: bool = True): method dismiss (line 160) | def dismiss(self): method revert (line 174) | def revert(self): FILE: api/models/thumbnail.py class Thumbnail (line 19) | class Thumbnail(models.Model): method _generate_thumbnail (line 29) | def _generate_thumbnail(self): method _calculate_aspect_ratio (line 110) | def _calculate_aspect_ratio(self): method _get_dominant_color (line 127) | def _get_dominant_color(self, palette_size=16): FILE: api/models/user.py function get_default_config_datetime_rules (line 11) | def get_default_config_datetime_rules(): # This is a callable function get_default_config_burst_detection_rules (line 15) | def get_default_config_burst_detection_rules(): # This is a callable function get_default_llm_settings (line 19) | def get_default_llm_settings(): function get_default_public_sharing_settings (line 34) | def get_default_public_sharing_settings(): class User (line 49) | class User(AbstractUser): class SaveMetadata (line 72) | class SaveMetadata(models.TextChoices): class FaceRecogniton (line 95) | class FaceRecogniton(models.TextChoices): class TextAlignment (line 107) | class TextAlignment(models.TextChoices): class HeaderSize (line 115) | class HeaderSize(models.TextChoices): class DuplicateSensitivity (line 128) | class DuplicateSensitivity(models.TextChoices): function get_admin_user (line 139) | def get_admin_user(): function get_deleted_user (line 143) | def get_deleted_user(): FILE: api/nextcloud.py function login (line 6) | def login(user): function list_dir (line 24) | def list_dir(user, path): FILE: api/perceptual_hash.py function calculate_perceptual_hash (line 21) | def calculate_perceptual_hash(image_path: str, hash_size: int = 8) -> st... function calculate_hash_from_thumbnail (line 47) | def calculate_hash_from_thumbnail(thumbnail_path: str) -> str | None: function hamming_distance (line 61) | def hamming_distance(hash1: str, hash2: str) -> int: function are_duplicates (line 81) | def are_duplicates(hash1: str, hash2: str, threshold: int = DEFAULT_HAMM... function find_similar_hashes (line 98) | def find_similar_hashes( FILE: api/permissions.py class IsAdminOrSelf (line 7) | class IsAdminOrSelf(permissions.BasePermission): method has_object_permission (line 8) | def has_object_permission(self, request, view, obj): class IsAdminOrFirstTimeSetupOrRegistrationAllowed (line 15) | class IsAdminOrFirstTimeSetupOrRegistrationAllowed(permissions.BasePermi... method has_permission (line 16) | def has_permission(self, request, view): class IsOwnerOrReadOnly (line 27) | class IsOwnerOrReadOnly(permissions.BasePermission): method has_object_permission (line 30) | def has_object_permission(self, request, view, obj): class IsUserOrReadOnly (line 40) | class IsUserOrReadOnly(permissions.BasePermission): method has_object_permission (line 43) | def has_object_permission(self, request, view, obj): class IsPhotoOrAlbumSharedTo (line 53) | class IsPhotoOrAlbumSharedTo(permissions.BasePermission): method has_object_permission (line 56) | def has_object_permission(self, request, view, obj): class IsRegistrationAllowed (line 70) | class IsRegistrationAllowed(permissions.BasePermission): method has_permission (line 73) | def has_permission(self, request, view): FILE: api/semantic_search.py function create_clip_embeddings (line 8) | def create_clip_embeddings(imgs): function calculate_query_embeddings (line 26) | def calculate_query_embeddings(query): FILE: api/serializers/PhotosGroupedByDate.py class PhotosGroupedByDate (line 7) | class PhotosGroupedByDate: method __init__ (line 8) | def __init__(self, location, date, photos): function get_photos_ordered_by_date (line 14) | def get_photos_ordered_by_date(photos): FILE: api/serializers/album_auto.py class AlbumAutoSerializer (line 9) | class AlbumAutoSerializer(serializers.ModelSerializer): class Meta (line 13) | class Meta: method get_people (line 27) | def get_people(self, obj) -> PersonSerializer(many=True): method delete (line 37) | def delete(self, validated_data, id): class AlbumAutoListSerializer (line 42) | class AlbumAutoListSerializer(serializers.ModelSerializer): class Meta (line 46) | class Meta: method get_photo_count (line 57) | def get_photo_count(self, obj) -> int: method get_photos (line 63) | def get_photos(self, obj) -> PhotoHashListSerializer: FILE: api/serializers/album_date.py class IncompleteAlbumDateSerializer (line 6) | class IncompleteAlbumDateSerializer(serializers.ModelSerializer): class Meta (line 14) | class Meta: method get_id (line 18) | def get_id(self, obj) -> str: method get_date (line 21) | def get_date(self, obj) -> str: method get_items (line 27) | def get_items(self, obj) -> list: method get_incomplete (line 30) | def get_incomplete(self, obj) -> bool: method get_number_of_items (line 33) | def get_number_of_items(self, obj) -> int: method get_location (line 39) | def get_location(self, obj) -> str: class AlbumDateSerializer (line 46) | class AlbumDateSerializer(serializers.ModelSerializer): class Meta (line 54) | class Meta: method get_id (line 58) | def get_id(self, obj) -> str: method get_date (line 61) | def get_date(self, obj) -> str: method get_items (line 67) | def get_items(self, obj) -> dict: method get_incomplete (line 71) | def get_incomplete(self, obj) -> bool: method get_number_of_items (line 74) | def get_number_of_items(self, obj) -> int: method get_location (line 78) | def get_location(self, obj) -> str: FILE: api/serializers/album_place.py class GroupedPlacePhotosSerializer (line 9) | class GroupedPlacePhotosSerializer(serializers.ModelSerializer): class Meta (line 13) | class Meta: method get_id (line 22) | def get_id(self, obj) -> str: method get_grouped_photos (line 25) | def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True): class AlbumPlaceSerializer (line 31) | class AlbumPlaceSerializer(serializers.ModelSerializer): class Meta (line 34) | class Meta: class AlbumPlaceListSerializer (line 39) | class AlbumPlaceListSerializer(serializers.ModelSerializer): class Meta (line 43) | class Meta: method get_photo_count (line 47) | def get_photo_count(self, obj) -> int: FILE: api/serializers/album_thing.py class GroupedThingPhotosSerializer (line 9) | class GroupedThingPhotosSerializer(serializers.ModelSerializer): class Meta (line 13) | class Meta: method get_id (line 21) | def get_id(self, obj) -> str: method get_grouped_photos (line 24) | def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True): class AlbumThingSerializer (line 30) | class AlbumThingSerializer(serializers.ModelSerializer): class Meta (line 33) | class Meta: class AlbumThingListSerializer (line 38) | class AlbumThingListSerializer(serializers.ModelSerializer): class Meta (line 42) | class Meta: method get_photo_count (line 53) | def get_photo_count(self, obj) -> int: FILE: api/serializers/album_user.py class AlbumUserSerializer (line 10) | class AlbumUserSerializer(serializers.ModelSerializer): class Meta (line 22) | class Meta: method get_id (line 39) | def get_id(self, obj) -> str: method get_grouped_photos (line 42) | def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True): method get_location (line 49) | def get_location(self, obj) -> str: method get_date (line 60) | def get_date(self, obj) -> str: method get_public (line 66) | def get_public(self, obj) -> bool: method get_public_slug (line 69) | def get_public_slug(self, obj) -> str: method get_public_expires_at (line 72) | def get_public_expires_at(self, obj): method get_public_sharing_options (line 75) | def get_public_sharing_options(self, obj) -> dict | None: class AlbumUserEditSerializer (line 89) | class AlbumUserEditSerializer(serializers.ModelSerializer): class Meta (line 99) | class Meta: method create (line 111) | def create(self, validated_data): method update (line 131) | def update(self, instance, validated_data): class AlbumUserListSerializer (line 168) | class AlbumUserListSerializer(serializers.ModelSerializer): class Meta (line 178) | class Meta: method get_cover_photo (line 195) | def get_cover_photo(self, obj) -> PhotoSuperSimpleSerializer: method get_photo_count (line 200) | def get_photo_count(self, obj) -> int: method get_public (line 206) | def get_public(self, obj) -> bool: method get_public_slug (line 209) | def get_public_slug(self, obj) -> str: method get_public_expires_at (line 212) | def get_public_expires_at(self, obj): method get_public_sharing_options (line 215) | def get_public_sharing_options(self, obj) -> dict | None: class AlbumUserPublicSerializer (line 229) | class AlbumUserPublicSerializer(serializers.ModelSerializer): class Meta (line 243) | class Meta: method get_id (line 256) | def get_id(self, obj) -> str: method _filtered_photos (line 259) | def _filtered_photos(self, obj): method get_grouped_photos (line 265) | def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True): method get_location (line 269) | def get_location(self, obj) -> str: method get_date (line 280) | def get_date(self, obj) -> str: FILE: api/serializers/face.py class PersonFaceListSerializer (line 6) | class PersonFaceListSerializer(serializers.ModelSerializer): class Meta (line 11) | class Meta: method get_person_label_probability (line 23) | def get_person_label_probability(self, obj): method get_face_url (line 29) | def get_face_url(self, obj): method get_photo_image_hash (line 32) | def get_photo_image_hash(self, obj): class IncompletePersonFaceListSerializer (line 36) | class IncompletePersonFaceListSerializer(serializers.ModelSerializer): class Meta (line 39) | class Meta: method get_face_count (line 43) | def get_face_count(self, obj) -> int: class FaceListSerializer (line 50) | class FaceListSerializer(serializers.ModelSerializer): class Meta (line 55) | class Meta: method get_person_label_probability (line 68) | def get_person_label_probability(self, obj) -> float: method get_face_url (line 71) | def get_face_url(self, obj) -> str: method get_person_name (line 74) | def get_person_name(self, obj) -> str: FILE: api/serializers/job.py class LongRunningJobSerializer (line 7) | class LongRunningJobSerializer(serializers.ModelSerializer): class Meta (line 11) | class Meta: method get_job_type_str (line 30) | def get_job_type_str(self, obj) -> str: FILE: api/serializers/person.py class GroupedPersonPhotosSerializer (line 10) | class GroupedPersonPhotosSerializer(serializers.ModelSerializer): class Meta (line 14) | class Meta: method get_id (line 22) | def get_id(self, obj) -> str: method get_grouped_photos (line 25) | def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True): class PersonSerializer (line 35) | class PersonSerializer(serializers.ModelSerializer): class Meta (line 42) | class Meta: method get_face_url (line 55) | def get_face_url(self, obj) -> str: method get_face_photo_url (line 62) | def get_face_photo_url(self, obj) -> str: method get_video (line 69) | def get_video(self, obj) -> str: method create (line 76) | def create(self, validated_data): method update (line 90) | def update(self, instance, validated_data): method delete (line 105) | def delete(self, validated_data, id): class AlbumPersonListSerializer (line 110) | class AlbumPersonListSerializer(serializers.ModelSerializer): class Meta (line 114) | class Meta: method get_photo_count (line 123) | def get_photo_count(self, obj) -> int: method get_cover_photo_url (line 126) | def get_cover_photo_url(self, obj) -> str: method get_face_photo_url (line 133) | def get_face_photo_url(self, obj) -> str: FILE: api/serializers/photo_metadata.py class MetadataFileSerializer (line 16) | class MetadataFileSerializer(serializers.ModelSerializer): class Meta (line 19) | class Meta: class MetadataEditSerializer (line 33) | class MetadataEditSerializer(serializers.ModelSerializer): class Meta (line 38) | class Meta: method get_user_name (line 53) | def get_user_name(self, obj) -> str: class PhotoMetadataSerializer (line 59) | class PhotoMetadataSerializer(serializers.ModelSerializer): class Meta (line 77) | class Meta: method get_edit_history (line 150) | def get_edit_history(self, obj) -> list: method get_sidecar_files (line 155) | def get_sidecar_files(self, obj) -> list: class PhotoMetadataUpdateSerializer (line 161) | class PhotoMetadataUpdateSerializer(serializers.ModelSerializer): class Meta (line 169) | class Meta: method update (line 191) | def update(self, instance, validated_data): class PhotoMetadataSummarySerializer (line 220) | class PhotoMetadataSummarySerializer(serializers.Serializer): method get_has_edits (line 249) | def get_has_edits(self, obj) -> bool: function get_backwards_compatible_metadata (line 254) | def get_backwards_compatible_metadata(photo: Photo) -> dict: FILE: api/serializers/photos.py class PhotoSummarySerializer (line 16) | class PhotoSummarySerializer(serializers.ModelSerializer): class Meta (line 35) | class Meta: method get_aspectRatio (line 59) | def get_aspectRatio(self, obj) -> float: method get_url (line 63) | def get_url(self, obj) -> str: method get_location (line 66) | def get_location(self, obj) -> str: method get_date (line 76) | def get_date(self, obj) -> str: method get_video_length (line 82) | def get_video_length(self, obj) -> int: method get_birthTime (line 89) | def get_birthTime(self, obj) -> str: method get_dominantColor (line 95) | def get_dominantColor(self, obj) -> str: method get_type (line 102) | def get_type(self, obj) -> str: method get_stacks (line 110) | def get_stacks(self, obj) -> list | None: method get_has_raw_variant (line 148) | def get_has_raw_variant(self, obj) -> bool: class GroupedPhotosSerializer (line 161) | class GroupedPhotosSerializer(serializers.ModelSerializer): class Meta (line 166) | class Meta: method get_date (line 170) | def get_date(self, obj) -> str: method get_location (line 173) | def get_location(self, obj) -> str: method get_items (line 176) | def get_items(self, obj) -> PhotoSummarySerializer(many=True): class PhotoEditSerializer (line 180) | class PhotoEditSerializer(serializers.ModelSerializer): class Meta (line 181) | class Meta: method update (line 197) | def update(self, instance, validated_data): class PhotoHashListSerializer (line 275) | class PhotoHashListSerializer(serializers.ModelSerializer): class Meta (line 276) | class Meta: class PhotoDetailsSummarySerializer (line 281) | class PhotoDetailsSummarySerializer(serializers.ModelSerializer): class Meta (line 286) | class Meta: method get_photo_summary (line 290) | def get_photo_summary(self, obj) -> PhotoSummarySerializer: method get_processing (line 293) | def get_processing(self, obj) -> bool: method get_album_date_id (line 296) | def get_album_date_id(self, obj) -> int: class PhotoSerializer (line 304) | class PhotoSerializer(serializers.ModelSerializer): class Meta (line 338) | class Meta: method _get_metadata (line 383) | def _get_metadata(self, obj) -> PhotoMetadata | None: method get_height (line 392) | def get_height(self, obj) -> int: method get_width (line 396) | def get_width(self, obj) -> int: method get_focal_length (line 400) | def get_focal_length(self, obj) -> float | None: method get_fstop (line 404) | def get_fstop(self, obj) -> float | None: method get_iso (line 408) | def get_iso(self, obj) -> int | None: method get_shutter_speed (line 412) | def get_shutter_speed(self, obj) -> str | None: method get_lens (line 416) | def get_lens(self, obj) -> str | None: method get_camera (line 420) | def get_camera(self, obj) -> str | None: method get_focalLength35Equivalent (line 424) | def get_focalLength35Equivalent(self, obj) -> int | None: method get_digitalZoomRatio (line 428) | def get_digitalZoomRatio(self, obj) -> float | None: method get_subjectDistance (line 432) | def get_subjectDistance(self, obj) -> float | None: method get_similar_photos (line 436) | def get_similar_photos(self, obj) -> list: method get_captions_json (line 452) | def get_captions_json(self, obj) -> dict: method get_search_captions (line 467) | def get_search_captions(self, obj) -> str: method get_search_location (line 472) | def get_search_location(self, obj) -> str: method get_image_path (line 477) | def get_image_path(self, obj) -> list[str]: method get_square_thumbnail_url (line 486) | def get_square_thumbnail_url(self, obj) -> str: method get_small_square_thumbnail_url (line 491) | def get_small_square_thumbnail_url(self, obj) -> str: method get_big_thumbnail_url (line 498) | def get_big_thumbnail_url(self, obj) -> str: method get_geolocation (line 501) | def get_geolocation(self, obj) -> dict: method get_people (line 507) | def get_people(self, obj) -> list: method get_embedded_media (line 557) | def get_embedded_media(self, obj: Photo) -> list[dict]: method get_metadata (line 575) | def get_metadata(self, obj: Photo) -> dict | None: method get_file_variants (line 593) | def get_file_variants(self, obj: Photo) -> list | None: method get_stacks (line 632) | def get_stacks(self, obj: Photo) -> list | None: class SharedFromMePhotoThroughSerializer (line 686) | class SharedFromMePhotoThroughSerializer(serializers.ModelSerializer): class Meta (line 690) | class Meta: method get_photo (line 694) | def get_photo(self, obj) -> PhotoSummarySerializer: class PublicPhotoDetailSerializer (line 698) | class PublicPhotoDetailSerializer(serializers.ModelSerializer): class Meta (line 741) | class Meta: method _get_sharing_settings (line 767) | def _get_sharing_settings(self) -> dict: method _get_metadata (line 771) | def _get_metadata(self, obj) -> PhotoMetadata | None: method get_square_thumbnail_url (line 781) | def get_square_thumbnail_url(self, obj) -> str: method get_small_square_thumbnail_url (line 784) | def get_small_square_thumbnail_url(self, obj) -> str: method get_big_thumbnail_url (line 787) | def get_big_thumbnail_url(self, obj) -> str: method get_exif_timestamp (line 791) | def get_exif_timestamp(self, obj): method get_exif_gps_lat (line 797) | def get_exif_gps_lat(self, obj): method get_exif_gps_lon (line 802) | def get_exif_gps_lon(self, obj): method get_geolocation_json (line 807) | def get_geolocation_json(self, obj): method get_search_location (line 812) | def get_search_location(self, obj) -> str: method get_camera (line 819) | def get_camera(self, obj) -> str | None: method get_lens (line 825) | def get_lens(self, obj) -> str | None: method get_focal_length (line 831) | def get_focal_length(self, obj) -> float | None: method get_fstop (line 837) | def get_fstop(self, obj) -> float | None: method get_iso (line 843) | def get_iso(self, obj) -> int | None: method get_shutter_speed (line 849) | def get_shutter_speed(self, obj) -> str | None: method get_width (line 855) | def get_width(self, obj) -> int: method get_height (line 861) | def get_height(self, obj) -> int: method get_search_captions (line 868) | def get_search_captions(self, obj) -> str: method get_captions_json (line 874) | def get_captions_json(self, obj) -> dict: method get_people (line 886) | def get_people(self, obj) -> list: FILE: api/serializers/simple.py class PhotoSuperSimpleSerializer (line 6) | class PhotoSuperSimpleSerializer(serializers.ModelSerializer): class Meta (line 7) | class Meta: class PhotoSimpleSerializer (line 12) | class PhotoSimpleSerializer(serializers.ModelSerializer): class Meta (line 15) | class Meta: method get_square_thumbnail (line 29) | def get_square_thumbnail(self, obj) -> str: class SimpleUserSerializer (line 37) | class SimpleUserSerializer(serializers.ModelSerializer): class Meta (line 38) | class Meta: FILE: api/serializers/user.py class UserSerializer (line 17) | class UserSerializer(serializers.ModelSerializer): class Meta (line 23) | class Meta: method validate_nextcloud_app_password (line 95) | def validate_nextcloud_app_password(self, value): method create (line 98) | def create(self, validated_data): method update (line 119) | def update(self, instance, validated_data): method get_photo_count (line 295) | def get_photo_count(self, obj) -> int: method get_public_photo_count (line 298) | def get_public_photo_count(self, obj) -> int: method get_public_photo_samples (line 301) | def get_public_photo_samples(self, obj) -> PhotoSuperSimpleSerializer(... method get_avatar_url (line 306) | def get_avatar_url(self, obj) -> str or None: class PublicUserSerializer (line 313) | class PublicUserSerializer(serializers.ModelSerializer): class Meta (line 318) | class Meta: method get_public_photo_count (line 330) | def get_public_photo_count(self, obj) -> int: method get_public_photo_samples (line 333) | def get_public_photo_samples(self, obj) -> PhotoSuperSimpleSerializer(... method get_avatar_url (line 338) | def get_avatar_url(self, obj) -> str or None: class SignupUserSerializer (line 345) | class SignupUserSerializer(serializers.ModelSerializer): class Meta (line 346) | class Meta: method create (line 369) | def create(self, validated_data): class DeleteUserSerializer (line 379) | class DeleteUserSerializer(serializers.ModelSerializer): class Meta (line 380) | class Meta: class ManageUserSerializer (line 385) | class ManageUserSerializer(serializers.ModelSerializer): class Meta (line 388) | class Meta: method get_photo_count (line 416) | def get_photo_count(self, obj) -> int: method update (line 419) | def update(self, instance: User, validated_data): FILE: api/services.py function check_services (line 39) | def check_services(): function is_healthy (line 51) | def is_healthy(service): function start_service (line 66) | def start_service(service): function stop_service (line 96) | def stop_service(service): function _is_arm_architecture (line 128) | def _is_arm_architecture(): function check_cpu_features (line 138) | def check_cpu_features(): function has_required_cpu_features (line 169) | def has_required_cpu_features(service): function is_service_compatible (line 217) | def is_service_compatible(service): function cleanup_deleted_photos (line 227) | def cleanup_deleted_photos(): FILE: api/social_graph.py function build_social_graph (line 8) | def build_social_graph(user): function build_ego_graph (line 46) | def build_ego_graph(person_id): FILE: api/stack_detection.py function clear_stacks_of_type (line 41) | def clear_stacks_of_type(user, stack_type): function detect_burst_sequences (line 71) | def detect_burst_sequences( function _detect_bursts_hard_criteria (line 132) | def _detect_bursts_hard_criteria(user, hard_rules, progress_callback=None): function _detect_bursts_soft_criteria (line 200) | def _detect_bursts_soft_criteria( function _create_burst_stack (line 282) | def _create_burst_stack(user, photos): function batch_detect_stacks (line 317) | def batch_detect_stacks(user, options=None): FILE: api/stacks/live_photo.py function _locate_google_embedded_video (line 38) | def _locate_google_embedded_video(data: bytes) -> int: function _locate_samsung_embedded_video (line 48) | def _locate_samsung_embedded_video(data: bytes) -> int: function has_embedded_motion_video (line 57) | def has_embedded_motion_video(path: str) -> bool: function extract_embedded_motion_video (line 88) | def extract_embedded_motion_video(path: str, output_hash: str) -> str | ... function find_apple_live_photo_video (line 129) | def find_apple_live_photo_video(image_path: str) -> str | None: function detect_live_photo (line 154) | def detect_live_photo(photo: "Photo", user: "User") -> PhotoStack | None: function _create_embedded_live_photo_stack (line 186) | def _create_embedded_live_photo_stack(photo: "Photo", user: "User") -> P... function _create_apple_live_photo_stack (line 223) | def _create_apple_live_photo_stack( function process_live_photos_batch (line 266) | def process_live_photos_batch(user: "User", photos: list["Photo"]) -> dict: FILE: api/stats.py function _is_sqlite (line 30) | def _is_sqlite() -> bool: function jump_by_month (line 34) | def jump_by_month(start_date, end_date, month_step=1): function median_value (line 46) | def median_value(queryset, term): function calc_megabytes (line 59) | def calc_megabytes(bytes): function get_server_stats (line 65) | def get_server_stats(): function get_count_stats (line 380) | def get_count_stats(user): function get_photo_month_counts (line 444) | def get_photo_month_counts(user): function get_searchterms_wordcloud (line 487) | def get_searchterms_wordcloud(user): function get_location_sunburst (line 603) | def get_location_sunburst(user): function get_location_clusters (line 665) | def get_location_clusters(user): function get_location_timeline (line 707) | def get_location_timeline(user): FILE: api/tests/test_api_robustness.py class DuplicatesAPIRobustnessTestCase (line 28) | class DuplicatesAPIRobustnessTestCase(TestCase): method setUp (line 31) | def setUp(self): method test_resolve_with_empty_body (line 40) | def test_resolve_with_empty_body(self): method test_resolve_with_invalid_photo_id (line 56) | def test_resolve_with_invalid_photo_id(self): method test_resolve_with_nonexistent_photo (line 70) | def test_resolve_with_nonexistent_photo(self): method test_access_nonexistent_duplicate (line 85) | def test_access_nonexistent_duplicate(self): method test_delete_nonexistent_duplicate (line 92) | def test_delete_nonexistent_duplicate(self): method test_list_with_invalid_status_filter (line 100) | def test_list_with_invalid_status_filter(self): method test_list_with_invalid_type_filter (line 106) | def test_list_with_invalid_type_filter(self): method test_extremely_long_string_in_query (line 111) | def test_extremely_long_string_in_query(self): method test_special_characters_in_query (line 118) | def test_special_characters_in_query(self): method test_unicode_in_query (line 123) | def test_unicode_in_query(self): method test_null_bytes_in_request (line 128) | def test_null_bytes_in_request(self): class StacksAPIRobustnessTestCase (line 143) | class StacksAPIRobustnessTestCase(TestCase): method setUp (line 146) | def setUp(self): method _create_photo (line 155) | def _create_photo(self, suffix): method test_create_manual_stack_with_empty_photos (line 169) | def test_create_manual_stack_with_empty_photos(self): method test_create_manual_stack_with_single_photo (line 179) | def test_create_manual_stack_with_single_photo(self): method test_create_manual_stack_with_invalid_photo_ids (line 191) | def test_create_manual_stack_with_invalid_photo_ids(self): method test_create_manual_stack_with_nonexistent_photos (line 200) | def test_create_manual_stack_with_nonexistent_photos(self): method test_create_manual_stack_with_other_user_photos (line 211) | def test_create_manual_stack_with_other_user_photos(self): method test_set_primary_with_photo_not_in_stack (line 238) | def test_set_primary_with_photo_not_in_stack(self): method test_add_photo_to_nonexistent_stack (line 260) | def test_add_photo_to_nonexistent_stack(self): method test_remove_all_photos_from_stack (line 273) | def test_remove_all_photos_from_stack(self): method test_merge_stacks_with_empty_list (line 294) | def test_merge_stacks_with_empty_list(self): method test_merge_single_stack (line 303) | def test_merge_single_stack(self): method test_list_with_invalid_stack_type_filter (line 322) | def test_list_with_invalid_stack_type_filter(self): class PhotoMetadataAPIRobustnessTestCase (line 328) | class PhotoMetadataAPIRobustnessTestCase(TestCase): method setUp (line 331) | def setUp(self): method test_update_with_invalid_field_types (line 352) | def test_update_with_invalid_field_types(self): method test_update_with_negative_values (line 362) | def test_update_with_negative_values(self): method test_update_with_extremely_large_numbers (line 372) | def test_update_with_extremely_large_numbers(self): method test_update_nonexistent_photo_metadata (line 381) | def test_update_nonexistent_photo_metadata(self): method test_update_other_user_photo_metadata (line 392) | def test_update_other_user_photo_metadata(self): method test_revert_nonexistent_edit (line 417) | def test_revert_nonexistent_edit(self): class AuthenticationRobustnessTestCase (line 427) | class AuthenticationRobustnessTestCase(TestCase): method setUp (line 430) | def setUp(self): method test_unauthenticated_access_to_duplicates (line 438) | def test_unauthenticated_access_to_duplicates(self): method test_unauthenticated_access_to_stacks (line 443) | def test_unauthenticated_access_to_stacks(self): method test_unauthenticated_detect_duplicates (line 448) | def test_unauthenticated_detect_duplicates(self): method test_unauthenticated_detect_stacks (line 453) | def test_unauthenticated_detect_stacks(self): class ConcurrentOperationsTestCase (line 459) | class ConcurrentOperationsTestCase(TestCase): method setUp (line 462) | def setUp(self): method _create_photo (line 471) | def _create_photo(self, suffix): method test_delete_already_deleted_duplicate (line 485) | def test_delete_already_deleted_duplicate(self): method test_delete_already_deleted_stack (line 500) | def test_delete_already_deleted_stack(self): method test_resolve_already_resolved_duplicate (line 517) | def test_resolve_already_resolved_duplicate(self): class BoundaryConditionsTestCase (line 542) | class BoundaryConditionsTestCase(TestCase): method setUp (line 545) | def setUp(self): method test_pagination_with_zero_page (line 554) | def test_pagination_with_zero_page(self): method test_pagination_with_negative_page (line 559) | def test_pagination_with_negative_page(self): method test_pagination_with_very_large_page (line 564) | def test_pagination_with_very_large_page(self): method test_pagination_with_invalid_page_size (line 570) | def test_pagination_with_invalid_page_size(self): method test_pagination_with_extremely_large_page_size (line 577) | def test_pagination_with_extremely_large_page_size(self): method test_stacks_pagination_with_zero_page (line 583) | def test_stacks_pagination_with_zero_page(self): class MalformedRequestTestCase (line 589) | class MalformedRequestTestCase(TestCase): method setUp (line 592) | def setUp(self): method test_post_with_invalid_json (line 601) | def test_post_with_invalid_json(self): method test_post_with_wrong_content_type (line 610) | def test_post_with_wrong_content_type(self): method test_get_with_duplicate_query_params (line 619) | def test_get_with_duplicate_query_params(self): FILE: api/tests/test_api_util.py function create_photos (line 11) | def create_photos(user): function compare_objects_with_ignored_props (line 16) | def compare_objects_with_ignored_props(result, expectation, ignore): class TestApiUtil (line 34) | class TestApiUtil(TestCase): method setUp (line 35) | def setUp(self) -> None: method test_wordcloud (line 40) | def test_wordcloud(self): method test_photo_month_count (line 56) | def test_photo_month_count(self): method test_photo_month_count_no_photos (line 69) | def test_photo_month_count_no_photos(self): method test_location_sunburst (line 74) | def test_location_sunburst(self): FILE: api/tests/test_auto_select_and_savings.py class PhotoStackAutoSelectPrimaryTestCase (line 21) | class PhotoStackAutoSelectPrimaryTestCase(TestCase): method setUp (line 24) | def setUp(self): method test_auto_select_empty_stack (line 27) | def test_auto_select_empty_stack(self): method test_auto_select_single_photo (line 38) | def test_auto_select_single_photo(self): method test_auto_select_raw_jpeg_prefers_jpeg (line 52) | def test_auto_select_raw_jpeg_prefers_jpeg(self): method test_auto_select_raw_jpeg_only_raw (line 73) | def test_auto_select_raw_jpeg_only_raw(self): method test_auto_select_burst_picks_middle (line 92) | def test_auto_select_burst_picks_middle(self): method test_auto_select_burst_no_timestamps (line 115) | def test_auto_select_burst_no_timestamps(self): method test_auto_select_manual_highest_resolution (line 135) | def test_auto_select_manual_highest_resolution(self): method test_auto_select_manual_no_metadata (line 167) | def test_auto_select_manual_no_metadata(self): class DuplicateAutoSelectBestTestCase (line 185) | class DuplicateAutoSelectBestTestCase(TestCase): method setUp (line 188) | def setUp(self): method test_auto_select_empty_duplicate (line 191) | def test_auto_select_empty_duplicate(self): method test_auto_select_exact_copy_shortest_path (line 202) | def test_auto_select_exact_copy_shortest_path(self): method test_auto_select_visual_duplicate_highest_resolution (line 230) | def test_auto_select_visual_duplicate_highest_resolution(self): method test_auto_select_visual_no_metadata (line 255) | def test_auto_select_visual_no_metadata(self): method test_auto_select_visual_partial_metadata (line 272) | def test_auto_select_visual_partial_metadata(self): class DuplicatePotentialSavingsTestCase (line 294) | class DuplicatePotentialSavingsTestCase(TestCase): method setUp (line 297) | def setUp(self): method test_savings_empty_duplicate (line 300) | def test_savings_empty_duplicate(self): method test_savings_single_photo (line 311) | def test_savings_single_photo(self): method test_savings_two_photos (line 328) | def test_savings_two_photos(self): method test_savings_many_photos (line 353) | def test_savings_many_photos(self): method test_savings_zero_sizes (line 380) | def test_savings_zero_sizes(self): method test_savings_updates_model_field (line 404) | def test_savings_updates_model_field(self): class DuplicateResolveRevertTestCase (line 434) | class DuplicateResolveRevertTestCase(TestCase): method setUp (line 437) | def setUp(self): method test_resolve_marks_status (line 440) | def test_resolve_marks_status(self): method test_resolve_trash_others (line 458) | def test_resolve_trash_others(self): method test_resolve_no_trash (line 484) | def test_resolve_no_trash(self): method test_revert_restores_trashed (line 502) | def test_revert_restores_trashed(self): method test_revert_clears_kept_photo (line 531) | def test_revert_clears_kept_photo(self): class DuplicateDismissTestCase (line 551) | class DuplicateDismissTestCase(TestCase): method setUp (line 554) | def setUp(self): method test_dismiss_sets_status (line 557) | def test_dismiss_sets_status(self): method test_dismiss_doesnt_trash (line 574) | def test_dismiss_doesnt_trash(self): FILE: api/tests/test_background_tasks.py class _FakeQuerySet (line 9) | class _FakeQuerySet(list): method count (line 10) | def count(self): class GeolocateLoggingTests (line 14) | class GeolocateLoggingTests(unittest.TestCase): method test_geolocate_logs_exception_without_crash (line 15) | def test_geolocate_logs_exception_without_crash(self): FILE: api/tests/test_bktree_and_duplicate_detection.py class BKTreeTestCase (line 26) | class BKTreeTestCase(TestCase): method test_empty_tree_search (line 29) | def test_empty_tree_search(self): method test_add_single_item (line 37) | def test_add_single_item(self): method test_add_multiple_items (line 45) | def test_add_multiple_items(self): method test_search_exact_match (line 54) | def test_search_exact_match(self): method test_search_within_threshold (line 66) | def test_search_within_threshold(self): method test_search_no_matches (line 81) | def test_search_no_matches(self): method test_hamming_distance_search (line 91) | def test_hamming_distance_search(self): class UnionFindTestCase (line 111) | class UnionFindTestCase(TestCase): method test_initial_state (line 114) | def test_initial_state(self): method test_union (line 122) | def test_union(self): method test_transitive_union (line 129) | def test_transitive_union(self): method test_get_groups (line 137) | def test_get_groups(self): class ExactCopyDetectionTestCase (line 151) | class ExactCopyDetectionTestCase(TestCase): method setUp (line 154) | def setUp(self): method _create_photo_with_hash (line 158) | def _create_photo_with_hash(self, file_hash, **kwargs): method test_no_duplicates (line 176) | def test_no_duplicates(self): method test_detect_exact_copies (line 186) | def test_detect_exact_copies(self): method test_excludes_trashed_photos (line 207) | def test_excludes_trashed_photos(self): method test_excludes_hidden_photos (line 228) | def test_excludes_hidden_photos(self): class VisualDuplicateDetectionTestCase (line 249) | class VisualDuplicateDetectionTestCase(TestCase): method setUp (line 252) | def setUp(self): method test_no_visual_duplicates (line 255) | def test_no_visual_duplicates(self): method test_detect_visual_duplicates (line 270) | def test_detect_visual_duplicates(self): method test_threshold_affects_detection (line 286) | def test_threshold_affects_detection(self): method test_skips_photos_without_phash (line 305) | def test_skips_photos_without_phash(self): method test_excludes_trashed_photos (line 319) | def test_excludes_trashed_photos(self): class BatchDetectionTestCase (line 335) | class BatchDetectionTestCase(TestCase): method setUp (line 338) | def setUp(self): method test_batch_detection_all_enabled (line 341) | def test_batch_detection_all_enabled(self): method test_batch_detection_exact_only (line 359) | def test_batch_detection_exact_only(self): method test_batch_detection_visual_only (line 374) | def test_batch_detection_visual_only(self): method test_batch_detection_with_clear_pending (line 390) | def test_batch_detection_with_clear_pending(self): method test_batch_detection_with_null_options (line 416) | def test_batch_detection_with_null_options(self): method test_batch_detection_with_empty_options (line 426) | def test_batch_detection_with_empty_options(self): class MultiUserDuplicateIsolationTestCase (line 437) | class MultiUserDuplicateIsolationTestCase(TestCase): method setUp (line 440) | def setUp(self): method test_detection_only_affects_own_photos (line 444) | def test_detection_only_affects_own_photos(self): method test_clearing_pending_only_affects_own (line 470) | def test_clearing_pending_only_affects_own(self): class DuplicateCreationEdgeCasesTestCase (line 494) | class DuplicateCreationEdgeCasesTestCase(TestCase): method setUp (line 497) | def setUp(self): method test_three_way_duplicates (line 500) | def test_three_way_duplicates(self): method test_many_duplicates_same_hash (line 520) | def test_many_duplicates_same_hash(self): FILE: api/tests/test_bulk_operations.py class BuildPhotoQuerysetTest (line 20) | class BuildPhotoQuerysetTest(TestCase): method setUp (line 23) | def setUp(self): method test_filters_by_owner (line 27) | def test_filters_by_owner(self): method test_filters_by_video (line 35) | def test_filters_by_video(self): method test_filters_by_photo (line 43) | def test_filters_by_photo(self): method test_filters_hidden (line 51) | def test_filters_hidden(self): method test_filters_in_trashcan (line 64) | def test_filters_in_trashcan(self): class BulkSetPhotosPublicTest (line 78) | class BulkSetPhotosPublicTest(TestCase): method setUp (line 81) | def setUp(self): method test_select_all_make_public (line 87) | def test_select_all_make_public(self): method test_select_all_make_private (line 109) | def test_select_all_make_private(self): method test_select_all_with_exclusions (line 131) | def test_select_all_with_exclusions(self): method test_select_all_only_affects_own_photos (line 161) | def test_select_all_only_affects_own_photos(self): class BulkSetPhotosHiddenTest (line 187) | class BulkSetPhotosHiddenTest(TestCase): method setUp (line 190) | def setUp(self): method test_select_all_hide_photos (line 195) | def test_select_all_hide_photos(self): method test_select_all_unhide_photos (line 217) | def test_select_all_unhide_photos(self): class BulkSetPhotosFavoriteTest (line 240) | class BulkSetPhotosFavoriteTest(TestCase): method setUp (line 243) | def setUp(self): method test_select_all_favorite_photos (line 248) | def test_select_all_favorite_photos(self): method test_select_all_unfavorite_photos (line 270) | def test_select_all_unfavorite_photos(self): class BulkSetPhotosDeletedTest (line 297) | class BulkSetPhotosDeletedTest(TestCase): method setUp (line 300) | def setUp(self): method test_select_all_move_to_trash (line 305) | def test_select_all_move_to_trash(self): method test_select_all_restore_from_trash (line 329) | def test_select_all_restore_from_trash(self): class BulkSharePhotosTest (line 354) | class BulkSharePhotosTest(TestCase): method setUp (line 357) | def setUp(self): method test_select_all_share_photos (line 363) | def test_select_all_share_photos(self): method test_select_all_unshare_photos (line 386) | def test_select_all_unshare_photos(self): method test_select_all_share_with_exclusions (line 417) | def test_select_all_share_with_exclusions(self): FILE: api/tests/test_burst_detection_rules.py class BurstRuleTypesTestCase (line 37) | class BurstRuleTypesTestCase(TestCase): method test_hard_criteria_types (line 40) | def test_hard_criteria_types(self): method test_soft_criteria_types (line 46) | def test_soft_criteria_types(self): class BurstRuleCategoryTestCase (line 52) | class BurstRuleCategoryTestCase(TestCase): method test_categories (line 55) | def test_categories(self): class BurstFilenamePatternTestCase (line 61) | class BurstFilenamePatternTestCase(TestCase): method test_burst_suffix_pattern (line 64) | def test_burst_suffix_pattern(self): method test_sequence_suffix_pattern (line 72) | def test_sequence_suffix_pattern(self): method test_bracketed_sequence_pattern (line 80) | def test_bracketed_sequence_pattern(self): method test_samsung_burst_pattern (line 88) | def test_samsung_burst_pattern(self): method test_iphone_burst_pattern (line 95) | def test_iphone_burst_pattern(self): class BurstDetectionRuleTestCase (line 103) | class BurstDetectionRuleTestCase(TestCase): method test_create_rule_with_minimal_params (line 106) | def test_create_rule_with_minimal_params(self): method test_create_rule_with_all_params (line 120) | def test_create_rule_with_all_params(self): method test_get_required_exif_tags_burst_mode (line 141) | def test_get_required_exif_tags_burst_mode(self): method test_get_required_exif_tags_sequence_number (line 155) | def test_get_required_exif_tags_sequence_number(self): method test_get_required_exif_tags_with_condition (line 169) | def test_get_required_exif_tags_with_condition(self): class RuleConditionTestCase (line 182) | class RuleConditionTestCase(TestCase): method test_check_condition_path_matches (line 185) | def test_check_condition_path_matches(self): method test_check_condition_path_no_condition (line 197) | def test_check_condition_path_no_condition(self): method test_check_condition_filename_matches (line 207) | def test_check_condition_filename_matches(self): method test_check_condition_exif_matches (line 219) | def test_check_condition_exif_matches(self): method test_check_condition_exif_invalid_format (line 232) | def test_check_condition_exif_invalid_format(self): method test_check_all_conditions_combined (line 243) | def test_check_all_conditions_combined(self): class ExifBurstModeRuleTestCase (line 276) | class ExifBurstModeRuleTestCase(TestCase): method _create_mock_photo (line 279) | def _create_mock_photo(self, path="/photos/test.jpg", timestamp=None): method test_burst_mode_on (line 287) | def test_burst_mode_on(self): method test_burst_mode_on_string (line 306) | def test_burst_mode_on_string(self): method test_continuous_drive_on (line 322) | def test_continuous_drive_on(self): method test_burst_mode_off (line 338) | def test_burst_mode_off(self): method test_disabled_rule_returns_false (line 354) | def test_disabled_rule_returns_false(self): class ExifSequenceNumberRuleTestCase (line 371) | class ExifSequenceNumberRuleTestCase(TestCase): method _create_mock_photo (line 374) | def _create_mock_photo(self, path="/photos/test.jpg", timestamp=None): method test_sequence_number_detected (line 382) | def test_sequence_number_detected(self): method test_sequence_number_zero (line 401) | def test_sequence_number_zero(self): method test_invalid_sequence_number (line 417) | def test_invalid_sequence_number(self): method test_no_sequence_number (line 433) | def test_no_sequence_number(self): class FilenamePatternRuleTestCase (line 448) | class FilenamePatternRuleTestCase(TestCase): method _create_mock_photo (line 451) | def _create_mock_photo(self, path): method test_burst_suffix_detected (line 459) | def test_burst_suffix_detected(self): method test_sequence_suffix_detected (line 477) | def test_sequence_suffix_detected(self): method test_bracketed_sequence_detected (line 492) | def test_bracketed_sequence_detected(self): method test_custom_pattern (line 507) | def test_custom_pattern(self): method test_specific_pattern_type (line 522) | def test_specific_pattern_type(self): method test_no_pattern_match (line 543) | def test_no_pattern_match(self): method test_no_main_file (line 558) | def test_no_main_file(self): method test_group_key_contains_directory (line 573) | def test_group_key_contains_directory(self): class GroupPhotosByTimestampTestCase (line 594) | class GroupPhotosByTimestampTestCase(TestCase): method _create_mock_photo (line 597) | def _create_mock_photo(self, timestamp, camera_make="Canon", camera_mo... method test_group_consecutive_photos (line 606) | def test_group_consecutive_photos(self): method test_separate_groups_by_time_gap (line 620) | def test_separate_groups_by_time_gap(self): method test_single_photo_not_grouped (line 637) | def test_single_photo_not_grouped(self): method test_photos_without_timestamp_skipped (line 645) | def test_photos_without_timestamp_skipped(self): method test_empty_photo_list (line 660) | def test_empty_photo_list(self): method test_require_same_camera (line 665) | def test_require_same_camera(self): method test_without_camera_requirement (line 684) | def test_without_camera_requirement(self): method test_custom_interval (line 699) | def test_custom_interval(self): class GroupPhotosByVisualSimilarityTestCase (line 716) | class GroupPhotosByVisualSimilarityTestCase(TestCase): method _create_mock_photo_with_hash (line 719) | def _create_mock_photo_with_hash(self, phash): method test_group_similar_photos (line 726) | def test_group_similar_photos(self, mock_hamming): method test_separate_dissimilar_photos (line 743) | def test_separate_dissimilar_photos(self, mock_hamming): method test_photos_without_hash_filtered (line 760) | def test_photos_without_hash_filtered(self): method test_empty_list (line 775) | def test_empty_list(self): method test_single_photo_with_hash (line 780) | def test_single_photo_with_hash(self): class DefaultRulesTestCase (line 788) | class DefaultRulesTestCase(TestCase): method test_default_hard_rules_count (line 791) | def test_default_hard_rules_count(self): method test_default_soft_rules_count (line 795) | def test_default_soft_rules_count(self): method test_default_hard_rules_enabled (line 799) | def test_default_hard_rules_enabled(self): method test_default_soft_rules_disabled (line 804) | def test_default_soft_rules_disabled(self): method test_all_default_rules_have_ids (line 809) | def test_all_default_rules_have_ids(self): method test_get_default_burst_detection_rules (line 815) | def test_get_default_burst_detection_rules(self): method test_get_all_predefined_burst_rules (line 820) | def test_get_all_predefined_burst_rules(self): class RuleFilteringTestCase (line 826) | class RuleFilteringTestCase(TestCase): method test_as_rules (line 829) | def test_as_rules(self): method test_get_hard_rules (line 841) | def test_get_hard_rules(self): method test_get_soft_rules (line 871) | def test_get_soft_rules(self): method test_get_enabled_rules (line 895) | def test_get_enabled_rules(self): class EdgeCasesTestCase (line 920) | class EdgeCasesTestCase(TestCase): method test_rule_with_none_timestamp (line 923) | def test_rule_with_none_timestamp(self): method test_group_key_consistency (line 945) | def test_group_key_consistency(self): method test_empty_exif_tags (line 966) | def test_empty_exif_tags(self): method test_special_characters_in_filename (line 983) | def test_special_characters_in_filename(self): method test_case_insensitive_pattern_matching (line 1002) | def test_case_insensitive_pattern_matching(self): FILE: api/tests/test_burst_filename_patterns.py class BurstFilenamePatternMatchingTestCase (line 27) | class BurstFilenamePatternMatchingTestCase(TestCase): method test_burst_suffix_pattern (line 30) | def test_burst_suffix_pattern(self): method test_sequence_suffix_pattern (line 43) | def test_sequence_suffix_pattern(self): method test_bracketed_sequence_pattern (line 58) | def test_bracketed_sequence_pattern(self): method test_samsung_burst_pattern (line 73) | def test_samsung_burst_pattern(self): method test_iphone_burst_pattern (line 83) | def test_iphone_burst_pattern(self): class CheckFilenamePatternTestCase (line 95) | class CheckFilenamePatternTestCase(TestCase): method setUp (line 98) | def setUp(self): method test_check_any_pattern_burst_suffix (line 101) | def test_check_any_pattern_burst_suffix(self): method test_check_any_pattern_sequence (line 112) | def test_check_any_pattern_sequence(self): method test_check_any_pattern_bracketed (line 121) | def test_check_any_pattern_bracketed(self): method test_check_specific_pattern (line 130) | def test_check_specific_pattern(self): method test_no_match (line 144) | def test_no_match(self): method test_group_key_includes_directory (line 154) | def test_group_key_includes_directory(self): method test_same_directory_same_base_grouped (line 172) | def test_same_directory_same_base_grouped(self): class GroupPhotosByTimestampTestCase (line 191) | class GroupPhotosByTimestampTestCase(TestCase): method setUp (line 194) | def setUp(self): method test_group_consecutive_timestamps (line 197) | def test_group_consecutive_timestamps(self): method test_separate_groups_by_gap (line 217) | def test_separate_groups_by_gap(self): method test_single_photo_no_group (line 245) | def test_single_photo_no_group(self): method test_empty_queryset (line 258) | def test_empty_queryset(self): method test_photos_without_timestamp (line 266) | def test_photos_without_timestamp(self): class BurstDetectionIntegrationTestCase (line 283) | class BurstDetectionIntegrationTestCase(TestCase): method setUp (line 286) | def setUp(self): method test_detect_burst_creates_stack (line 289) | def test_detect_burst_creates_stack(self): method test_case_insensitive_pattern_matching (line 316) | def test_case_insensitive_pattern_matching(self): class FilenamePatternEdgeCasesTestCase (line 333) | class FilenamePatternEdgeCasesTestCase(TestCase): method setUp (line 336) | def setUp(self): method test_no_extension (line 339) | def test_no_extension(self): method test_multiple_extensions (line 349) | def test_multiple_extensions(self): method test_unicode_filename (line 358) | def test_unicode_filename(self): method test_very_long_filename (line 367) | def test_very_long_filename(self): method test_special_characters_in_path (line 377) | def test_special_characters_in_path(self): method test_invalid_pattern_type (line 386) | def test_invalid_pattern_type(self): FILE: api/tests/test_delete_photos.py class DeletePhotosTest (line 9) | class DeletePhotosTest(TestCase): method setUp (line 10) | def setUp(self): method test_tag_my_photos_for_removal (line 16) | def test_tag_my_photos_for_removal(self): method test_untag_my_photos_for_removal (line 32) | def test_untag_my_photos_for_removal(self): method test_tag_photos_of_other_user_for_removal (line 51) | def test_tag_photos_of_other_user_for_removal(self): method test_tag_for_removal_nonexistent_photo (line 69) | def test_tag_for_removal_nonexistent_photo(self, logger): method test_delete_tagged_photos_for_removal (line 85) | def test_delete_tagged_photos_for_removal(self): method test_delete_tagged_photos_of_other_user_for_removal (line 104) | def test_delete_tagged_photos_of_other_user_for_removal(self): FILE: api/tests/test_detection_edge_cases.py class BurstRuleParsingTestCase (line 32) | class BurstRuleParsingTestCase(TestCase): method test_parse_valid_rule (line 35) | def test_parse_valid_rule(self): method test_parse_rule_with_missing_optional_fields (line 51) | def test_parse_rule_with_missing_optional_fields(self): method test_parse_disabled_rule (line 64) | def test_parse_disabled_rule(self): method test_default_rules_are_valid (line 75) | def test_default_rules_are_valid(self): class BurstFilenamePatternTestCase (line 84) | class BurstFilenamePatternTestCase(TestCase): method test_burst_suffix_pattern (line 87) | def test_burst_suffix_pattern(self): method test_sequence_suffix_pattern (line 100) | def test_sequence_suffix_pattern(self): method test_bracketed_sequence_pattern (line 112) | def test_bracketed_sequence_pattern(self): class UserBurstRulesTestCase (line 126) | class UserBurstRulesTestCase(TestCase): method setUp (line 129) | def setUp(self): method test_as_rules_with_default_rules (line 132) | def test_as_rules_with_default_rules(self): method test_as_rules_with_custom_rules (line 140) | def test_as_rules_with_custom_rules(self): method test_as_rules_with_empty_list (line 155) | def test_as_rules_with_empty_list(self): method test_user_rules_stored_as_json (line 161) | def test_user_rules_stored_as_json(self): class RawJpegDetectionEdgeCasesTestCase (line 182) | class RawJpegDetectionEdgeCasesTestCase(TestCase): method setUp (line 189) | def setUp(self): method test_detection_with_no_raw_photos (line 192) | def test_detection_with_no_raw_photos(self): method test_detection_with_raw_no_matching_jpeg (line 206) | def test_detection_with_raw_no_matching_jpeg(self): method test_detection_case_insensitive_extensions (line 226) | def test_detection_case_insensitive_extensions(self): method test_detection_with_photo_no_main_file (line 247) | def test_detection_with_photo_no_main_file(self): method test_detection_clears_existing_stacks (line 261) | def test_detection_clears_existing_stacks(self): class BurstDetectionEdgeCasesTestCase (line 288) | class BurstDetectionEdgeCasesTestCase(TestCase): method setUp (line 291) | def setUp(self): method test_detection_with_no_photos (line 294) | def test_detection_with_no_photos(self): method test_detection_with_all_rules_disabled (line 302) | def test_detection_with_all_rules_disabled(self): method test_detection_with_trashed_photos_excluded (line 326) | def test_detection_with_trashed_photos_excluded(self): class TimestampProximityRuleTestCase (line 347) | class TimestampProximityRuleTestCase(TestCase): method setUp (line 350) | def setUp(self): method test_photos_within_threshold_grouped (line 353) | def test_photos_within_threshold_grouped(self): method test_photos_beyond_threshold_not_grouped (line 389) | def test_photos_beyond_threshold_not_grouped(self): method test_photos_without_timestamp_skipped (line 421) | def test_photos_without_timestamp_skipped(self): class LivePhotoDetectionEdgeCasesTestCase (line 450) | class LivePhotoDetectionEdgeCasesTestCase(TestCase): method setUp (line 457) | def setUp(self): method test_detection_with_no_live_photos (line 460) | def test_detection_with_no_live_photos(self): class DetectionProgressCallbackTestCase (line 473) | class DetectionProgressCallbackTestCase(TestCase): method setUp (line 476) | def setUp(self): method test_raw_jpeg_detection_calls_progress (line 480) | def test_raw_jpeg_detection_calls_progress(self): method test_burst_detection_calls_progress (line 504) | def test_burst_detection_calls_progress(self): class BatchDetectionEdgeCasesTestCase (line 524) | class BatchDetectionEdgeCasesTestCase(TestCase): method setUp (line 527) | def setUp(self): method test_batch_detection_all_types (line 530) | def test_batch_detection_all_types(self): method test_batch_detection_none_enabled (line 549) | def test_batch_detection_none_enabled(self): method test_batch_detection_with_null_options (line 568) | def test_batch_detection_with_null_options(self): method test_batch_detection_with_empty_options (line 581) | def test_batch_detection_with_empty_options(self): class MultiUserDetectionIsolationTestCase (line 595) | class MultiUserDetectionIsolationTestCase(TestCase): method setUp (line 598) | def setUp(self): method test_detection_only_affects_own_photos (line 602) | def test_detection_only_affects_own_photos(self): method test_clearing_stacks_only_affects_own (line 618) | def test_clearing_stacks_only_affects_own(self): FILE: api/tests/test_directory_watcher_fix.py class DirectoryWatcherFixTest (line 8) | class DirectoryWatcherFixTest(TestCase): method setUp (line 9) | def setUp(self): method test_generate_tags_query_works (line 12) | def test_generate_tags_query_works(self): method test_generate_tags_query_excludes_photos_with_places365 (line 41) | def test_generate_tags_query_excludes_photos_with_places365(self): FILE: api/tests/test_dirtree.py class DirTreeTest (line 9) | class DirTreeTest(TestCase): method setUp (line 10) | def setUp(self): method test_admin_should_allow_to_retrieve_dirtree (line 17) | def test_admin_should_allow_to_retrieve_dirtree(self): method test_should_retrieve_dir_listing_by_path (line 22) | def test_should_retrieve_dir_listing_by_path(self): method test_should_fail_when_listing_with_invalid_path (line 27) | def test_should_fail_when_listing_with_invalid_path(self): method test_children_list_should_be_alphabetical_case_insensitive (line 36) | def test_children_list_should_be_alphabetical_case_insensitive(self): method test_regular_user_is_not_allowed_to_retrieve_dirtree (line 53) | def test_regular_user_is_not_allowed_to_retrieve_dirtree(self): method test_anonymous_user_is_not_allower_to_retrieve_dirtree (line 58) | def test_anonymous_user_is_not_allower_to_retrieve_dirtree(self): FILE: api/tests/test_duplicate_api_edge_cases.py class DuplicateResolveEdgeCasesTestCase (line 21) | class DuplicateResolveEdgeCasesTestCase(TestCase): method setUp (line 24) | def setUp(self): method test_resolve_already_resolved_duplicate (line 29) | def test_resolve_already_resolved_duplicate(self): method test_resolve_with_photo_already_trashed (line 54) | def test_resolve_with_photo_already_trashed(self): method test_resolve_with_trash_others_false (line 77) | def test_resolve_with_trash_others_false(self): method test_resolve_with_nonexistent_photo_hash (line 109) | def test_resolve_with_nonexistent_photo_hash(self): method test_resolve_empty_request (line 128) | def test_resolve_empty_request(self): class DuplicateRevertEdgeCasesTestCase (line 147) | class DuplicateRevertEdgeCasesTestCase(TestCase): method setUp (line 150) | def setUp(self): method test_revert_pending_duplicate (line 155) | def test_revert_pending_duplicate(self): method test_revert_dismissed_duplicate (line 172) | def test_revert_dismissed_duplicate(self): method test_revert_when_photos_permanently_deleted (line 189) | def test_revert_when_photos_permanently_deleted(self): method test_revert_multiple_times (line 220) | def test_revert_multiple_times(self): class DuplicateDeleteEdgeCasesTestCase (line 243) | class DuplicateDeleteEdgeCasesTestCase(TestCase): method setUp (line 250) | def setUp(self): method test_delete_duplicate_unlinks_all_photos (line 255) | def test_delete_duplicate_unlinks_all_photos(self): method test_delete_duplicate_with_many_photos (line 282) | def test_delete_duplicate_with_many_photos(self): method test_delete_resolved_duplicate (line 298) | def test_delete_resolved_duplicate(self): method test_delete_nonexistent_duplicate (line 316) | def test_delete_nonexistent_duplicate(self): method test_delete_other_users_duplicate (line 321) | def test_delete_other_users_duplicate(self): class DuplicateDetailEdgeCasesTestCase (line 339) | class DuplicateDetailEdgeCasesTestCase(TestCase): method setUp (line 342) | def setUp(self): method test_detail_with_photos_without_metadata (line 347) | def test_detail_with_photos_without_metadata(self): method test_detail_with_photos_without_main_file (line 370) | def test_detail_with_photos_without_main_file(self): method test_detail_with_deleted_kept_photo (line 388) | def test_detail_with_deleted_kept_photo(self): class DuplicateListEdgeCasesTestCase (line 417) | class DuplicateListEdgeCasesTestCase(TestCase): method setUp (line 420) | def setUp(self): method test_list_excludes_single_photo_duplicates (line 425) | def test_list_excludes_single_photo_duplicates(self): method test_list_with_kept_photo_deleted (line 441) | def test_list_with_kept_photo_deleted(self): method test_list_pagination_edge_cases (line 468) | def test_list_pagination_edge_cases(self): class DuplicateAutoSelectBestEdgeCasesTestCase (line 493) | class DuplicateAutoSelectBestEdgeCasesTestCase(TestCase): method setUp (line 496) | def setUp(self): method test_auto_select_with_no_photos (line 499) | def test_auto_select_with_no_photos(self): method test_auto_select_exact_copy_all_null_paths (line 509) | def test_auto_select_exact_copy_all_null_paths(self): method test_auto_select_visual_duplicate_no_metadata (line 528) | def test_auto_select_visual_duplicate_no_metadata(self): class DuplicateDismissEdgeCasesTestCase (line 547) | class DuplicateDismissEdgeCasesTestCase(TestCase): method setUp (line 550) | def setUp(self): method test_dismiss_already_dismissed (line 555) | def test_dismiss_already_dismissed(self): method test_dismiss_resolved_duplicate (line 573) | def test_dismiss_resolved_duplicate(self): class DuplicateStatsEdgeCasesTestCase (line 596) | class DuplicateStatsEdgeCasesTestCase(TestCase): method setUp (line 599) | def setUp(self): method test_stats_with_no_duplicates (line 604) | def test_stats_with_no_duplicates(self): method test_stats_counts_by_type (line 614) | def test_stats_counts_by_type(self): method test_stats_counts_by_status (line 642) | def test_stats_counts_by_status(self): method test_stats_potential_savings (line 677) | def test_stats_potential_savings(self): method test_stats_photos_in_duplicates (line 696) | def test_stats_photos_in_duplicates(self): method test_stats_other_users_not_included (line 720) | def test_stats_other_users_not_included(self): FILE: api/tests/test_duplicate_detection.py class DuplicateModelTest (line 20) | class DuplicateModelTest(TestCase): method setUp (line 23) | def setUp(self): method test_create_duplicate_group (line 27) | def test_create_duplicate_group(self): method test_create_duplicate_with_less_than_2_photos_returns_none (line 39) | def test_create_duplicate_with_less_than_2_photos_returns_none(self): method test_create_or_merge_creates_new_duplicate (line 48) | def test_create_or_merge_creates_new_duplicate(self): method test_create_or_merge_merges_existing (line 60) | def test_create_or_merge_merges_existing(self): method test_resolve_duplicate_trashes_others (line 78) | def test_resolve_duplicate_trashes_others(self): method test_resolve_duplicate_without_trashing (line 98) | def test_resolve_duplicate_without_trashing(self): method test_dismiss_duplicate (line 115) | def test_dismiss_duplicate(self): method test_revert_resolved_duplicate (line 129) | def test_revert_resolved_duplicate(self): method test_revert_non_resolved_duplicate_returns_zero (line 152) | def test_revert_non_resolved_duplicate_returns_zero(self): method test_auto_select_best_photo_exact_copy (line 162) | def test_auto_select_best_photo_exact_copy(self): method test_calculate_potential_savings (line 172) | def test_calculate_potential_savings(self): method test_merge_duplicates (line 190) | def test_merge_duplicates(self): class DuplicateAPITest (line 215) | class DuplicateAPITest(TestCase): method setUp (line 218) | def setUp(self): method test_list_duplicates_empty (line 225) | def test_list_duplicates_empty(self): method test_list_duplicates_with_results (line 233) | def test_list_duplicates_with_results(self): method test_list_duplicates_excludes_other_users (line 245) | def test_list_duplicates_excludes_other_users(self): method test_list_duplicates_filter_by_type (line 259) | def test_list_duplicates_filter_by_type(self): method test_list_duplicates_filter_by_status (line 278) | def test_list_duplicates_filter_by_status(self): method test_get_duplicate_detail (line 297) | def test_get_duplicate_detail(self): method test_get_duplicate_detail_not_found (line 310) | def test_get_duplicate_detail_not_found(self): method test_get_duplicate_detail_wrong_user (line 316) | def test_get_duplicate_detail_wrong_user(self): method test_resolve_duplicate (line 327) | def test_resolve_duplicate(self): method test_resolve_duplicate_missing_photo_hash (line 348) | def test_resolve_duplicate_missing_photo_hash(self): method test_resolve_duplicate_invalid_photo (line 362) | def test_resolve_duplicate_invalid_photo(self): method test_dismiss_duplicate (line 380) | def test_dismiss_duplicate(self): method test_revert_duplicate (line 392) | def test_revert_duplicate(self): method test_revert_non_resolved_duplicate_fails (line 407) | def test_revert_non_resolved_duplicate_fails(self): method test_delete_duplicate (line 417) | def test_delete_duplicate(self): method test_get_duplicate_stats (line 435) | def test_get_duplicate_stats(self): method test_detect_duplicates (line 450) | def test_detect_duplicates(self): class DuplicateEdgeCasesTest (line 465) | class DuplicateEdgeCasesTest(TestCase): method setUp (line 468) | def setUp(self): method test_duplicate_with_single_photo_excluded_from_list (line 474) | def test_duplicate_with_single_photo_excluded_from_list(self): method test_resolve_already_resolved_duplicate (line 487) | def test_resolve_already_resolved_duplicate(self): method test_photo_in_multiple_duplicate_groups (line 506) | def test_photo_in_multiple_duplicate_groups(self): method test_delete_photo_removes_from_duplicate (line 523) | def test_delete_photo_removes_from_duplicate(self): method test_pagination_works_correctly (line 539) | def test_pagination_works_correctly(self): method test_invalid_uuid_format (line 563) | def test_invalid_uuid_format(self): method test_concurrent_resolve_same_duplicate (line 575) | def test_concurrent_resolve_same_duplicate(self): class BKTreeTest (line 610) | class BKTreeTest(TestCase): method test_bk_tree_basic_operations (line 613) | def test_bk_tree_basic_operations(self): method test_bk_tree_empty_search (line 630) | def test_bk_tree_empty_search(self): class UnionFindTest (line 640) | class UnionFindTest(TestCase): method test_union_find_basic (line 643) | def test_union_find_basic(self): method test_union_find_get_groups (line 655) | def test_union_find_get_groups(self): method test_union_find_single_elements_not_returned (line 670) | def test_union_find_single_elements_not_returned(self): FILE: api/tests/test_duplicate_detection_logic.py class BKTreeTestCase (line 31) | class BKTreeTestCase(TestCase): method setUp (line 34) | def setUp(self): method test_empty_tree_search_returns_empty (line 40) | def test_empty_tree_search_returns_empty(self): method test_add_single_item (line 45) | def test_add_single_item(self): method test_add_multiple_items (line 54) | def test_add_multiple_items(self): method test_search_exact_match (line 62) | def test_search_exact_match(self): method test_search_within_threshold (line 73) | def test_search_within_threshold(self): method test_search_threshold_excludes_distant (line 87) | def test_search_threshold_excludes_distant(self): method test_search_returns_correct_distances (line 97) | def test_search_returns_correct_distances(self): method test_add_duplicate_hash (line 110) | def test_add_duplicate_hash(self): method test_large_tree_performance (line 122) | def test_large_tree_performance(self): class UnionFindTestCase (line 136) | class UnionFindTestCase(TestCase): method test_initial_find_creates_entry (line 139) | def test_initial_find_creates_entry(self): method test_find_same_element_returns_itself (line 148) | def test_find_same_element_returns_itself(self): method test_union_links_elements (line 157) | def test_union_links_elements(self): method test_union_multiple_elements (line 165) | def test_union_multiple_elements(self): method test_union_separate_groups (line 179) | def test_union_separate_groups(self): method test_get_groups_returns_groups (line 190) | def test_get_groups_returns_groups(self): method test_get_groups_excludes_singletons (line 207) | def test_get_groups_excludes_singletons(self): method test_path_compression (line 219) | def test_path_compression(self): class DetectExactCopiesTestCase (line 233) | class DetectExactCopiesTestCase(TestCase): method setUp (line 236) | def setUp(self): method _create_photo_with_hash (line 239) | def _create_photo_with_hash(self, image_hash, file_hash=None, **kwargs): method test_no_duplicates_returns_zero (line 256) | def test_no_duplicates_returns_zero(self): method test_detects_duplicate_image_hash (line 266) | def test_detects_duplicate_image_hash(self): method test_detects_duplicate_file_hash (line 281) | def test_detects_duplicate_file_hash(self): method test_skips_hidden_photos (line 294) | def test_skips_hidden_photos(self): method test_skips_trashed_photos (line 304) | def test_skips_trashed_photos(self): method test_multiple_duplicate_groups (line 313) | def test_multiple_duplicate_groups(self): method test_progress_callback_called (line 328) | def test_progress_callback_called(self): class DetectVisualDuplicatesTestCase (line 343) | class DetectVisualDuplicatesTestCase(TestCase): method setUp (line 346) | def setUp(self): method _create_photo_with_phash (line 349) | def _create_photo_with_phash(self, perceptual_hash, **kwargs): method test_no_photos_returns_zero (line 356) | def test_no_photos_returns_zero(self): method test_single_photo_returns_zero (line 362) | def test_single_photo_returns_zero(self): method test_detects_identical_phash (line 370) | def test_detects_identical_phash(self): method test_detects_similar_phash_within_threshold (line 379) | def test_detects_similar_phash_within_threshold(self): method test_threshold_excludes_dissimilar (line 389) | def test_threshold_excludes_dissimilar(self): method test_skips_photos_without_phash (line 398) | def test_skips_photos_without_phash(self): method test_skips_hidden_photos (line 409) | def test_skips_hidden_photos(self): method test_creates_visual_duplicate_type (line 418) | def test_creates_visual_duplicate_type(self): class BatchDetectDuplicatesTestCase (line 429) | class BatchDetectDuplicatesTestCase(TestCase): method setUp (line 432) | def setUp(self): method test_calls_both_detectors_by_default (line 437) | def test_calls_both_detectors_by_default(self, mock_visual, mock_exact): method test_respects_options (line 449) | def test_respects_options(self, mock_visual, mock_exact): method test_passes_visual_threshold (line 464) | def test_passes_visual_threshold(self, mock_visual, mock_exact): method test_creates_job (line 479) | def test_creates_job(self, mock_visual, mock_exact): method test_clear_pending_option (line 494) | def test_clear_pending_option(self, mock_visual, mock_exact): method test_handles_exception (line 515) | def test_handles_exception(self, mock_exact): class EdgeCasesTestCase (line 527) | class EdgeCasesTestCase(TestCase): method setUp (line 530) | def setUp(self): method test_empty_photo_library (line 533) | def test_empty_photo_library(self): method test_photo_without_files (line 541) | def test_photo_without_files(self): method test_photo_with_short_file_hash (line 551) | def test_photo_with_short_file_hash(self): method test_different_users_isolated (line 566) | def test_different_users_isolated(self): method test_metadata_files_excluded (line 584) | def test_metadata_files_excluded(self): method test_bktree_with_empty_hash (line 601) | def test_bktree_with_empty_hash(self): method test_union_find_with_same_element_union (line 615) | def test_union_find_with_same_element_union(self): method test_three_way_duplicate (line 625) | def test_three_way_duplicate(self): method test_transitive_duplicates_merged (line 640) | def test_transitive_duplicates_merged(self): FILE: api/tests/test_duplicate_filtering.py class DuplicateFilterByTypeTestCase (line 19) | class DuplicateFilterByTypeTestCase(TestCase): method setUp (line 22) | def setUp(self): method test_filter_exact_copies (line 52) | def test_filter_exact_copies(self): method test_filter_visual_duplicates (line 63) | def test_filter_visual_duplicates(self): method test_filter_invalid_type (line 76) | def test_filter_invalid_type(self): method test_no_filter_returns_all (line 84) | def test_no_filter_returns_all(self): class DuplicateFilterByStatusTestCase (line 92) | class DuplicateFilterByStatusTestCase(TestCase): method setUp (line 95) | def setUp(self): method test_filter_pending (line 127) | def test_filter_pending(self): method test_filter_resolved (line 140) | def test_filter_resolved(self): method test_filter_dismissed (line 149) | def test_filter_dismissed(self): method test_combined_type_and_status_filter (line 158) | def test_combined_type_and_status_filter(self): class PhotosWithoutPerceptualHashTestCase (line 168) | class PhotosWithoutPerceptualHashTestCase(TestCase): method setUp (line 171) | def setUp(self): method test_detection_handles_null_perceptual_hash (line 176) | def test_detection_handles_null_perceptual_hash(self): method test_visual_duplicate_detection_endpoint (line 195) | def test_visual_duplicate_detection_endpoint(self): class DuplicateDetectionJobTestCase (line 217) | class DuplicateDetectionJobTestCase(TestCase): method setUp (line 220) | def setUp(self): method test_detect_with_all_options (line 225) | def test_detect_with_all_options(self): method test_detect_exact_only (line 240) | def test_detect_exact_only(self): method test_detect_visual_only (line 250) | def test_detect_visual_only(self): method test_detect_nothing_returns_error (line 260) | def test_detect_nothing_returns_error(self): method test_detect_with_clear_pending (line 271) | def test_detect_with_clear_pending(self): class VisualThresholdTestCase (line 294) | class VisualThresholdTestCase(TestCase): method setUp (line 297) | def setUp(self): method test_strict_threshold (line 302) | def test_strict_threshold(self): method test_loose_threshold (line 312) | def test_loose_threshold(self): method test_zero_threshold (line 322) | def test_zero_threshold(self): method test_negative_threshold_handled (line 332) | def test_negative_threshold_handled(self): class DuplicateListSortingTestCase (line 344) | class DuplicateListSortingTestCase(TestCase): method setUp (line 347) | def setUp(self): method test_default_sorting (line 368) | def test_default_sorting(self): method test_sort_by_created_at (line 375) | def test_sort_by_created_at(self): class DuplicateBulkActionsTestCase (line 382) | class DuplicateBulkActionsTestCase(TestCase): method setUp (line 385) | def setUp(self): method test_bulk_dismiss (line 390) | def test_bulk_dismiss(self): class EmptyDuplicateGroupTestCase (line 414) | class EmptyDuplicateGroupTestCase(TestCase): method setUp (line 417) | def setUp(self): method test_duplicate_with_no_photos (line 422) | def test_duplicate_with_no_photos(self): method test_duplicate_with_one_photo (line 435) | def test_duplicate_with_one_photo(self): method test_resolve_single_photo_duplicate (line 450) | def test_resolve_single_photo_duplicate(self): FILE: api/tests/test_edge_cases_integration.py class PhotoNoExifDataTestCase (line 21) | class PhotoNoExifDataTestCase(TestCase): method setUp (line 24) | def setUp(self): method test_photo_without_exif_timestamp (line 27) | def test_photo_without_exif_timestamp(self): method test_photo_without_gps_data (line 37) | def test_photo_without_gps_data(self): method test_photo_without_perceptual_hash (line 47) | def test_photo_without_perceptual_hash(self): method test_stack_with_no_exif_photos (line 57) | def test_stack_with_no_exif_photos(self): method test_duplicate_with_no_metadata_photos (line 77) | def test_duplicate_with_no_metadata_photos(self): method test_burst_detection_no_timestamps (line 96) | def test_burst_detection_no_timestamps(self): class MissingFileTestCase (line 109) | class MissingFileTestCase(TestCase): method setUp (line 112) | def setUp(self): method test_photo_with_null_main_file (line 115) | def test_photo_with_null_main_file(self): method test_stack_photos_with_missing_metadata (line 124) | def test_stack_photos_with_missing_metadata(self): class ConcurrentDetectionTestCase (line 141) | class ConcurrentDetectionTestCase(TransactionTestCase): method setUp (line 144) | def setUp(self): method test_concurrent_duplicate_detection_requests (line 149) | def test_concurrent_duplicate_detection_requests(self): method test_concurrent_stack_detection_requests (line 176) | def test_concurrent_stack_detection_requests(self): class EmptyDataTestCase (line 193) | class EmptyDataTestCase(APITestCase): method setUp (line 196) | def setUp(self): method test_duplicate_list_empty (line 201) | def test_duplicate_list_empty(self): method test_stack_list_empty (line 207) | def test_stack_list_empty(self): method test_duplicate_stats_empty (line 213) | def test_duplicate_stats_empty(self): method test_stack_stats_empty (line 218) | def test_stack_stats_empty(self): method test_detection_with_no_photos (line 223) | def test_detection_with_no_photos(self): method test_stack_detection_with_no_photos (line 229) | def test_stack_detection_with_no_photos(self): class InvalidDataTestCase (line 236) | class InvalidDataTestCase(APITestCase): method setUp (line 239) | def setUp(self): method test_resolve_with_invalid_photo_id (line 244) | def test_resolve_with_invalid_photo_id(self): method test_add_to_stack_with_invalid_photo_ids (line 260) | def test_add_to_stack_with_invalid_photo_ids(self): method test_set_primary_with_invalid_photo_id (line 277) | def test_set_primary_with_invalid_photo_id(self): method test_detection_with_invalid_options (line 293) | def test_detection_with_invalid_options(self): class SinglePhotoGroupTestCase (line 304) | class SinglePhotoGroupTestCase(TestCase): method setUp (line 307) | def setUp(self): method test_duplicate_with_single_photo_deleted (line 310) | def test_duplicate_with_single_photo_deleted(self): method test_stack_with_single_photo (line 325) | def test_stack_with_single_photo(self): method test_auto_select_with_single_photo (line 337) | def test_auto_select_with_single_photo(self): class PhotoDeletionEdgeCasesTestCase (line 353) | class PhotoDeletionEdgeCasesTestCase(TestCase): method setUp (line 356) | def setUp(self): method test_delete_photo_in_multiple_stacks (line 359) | def test_delete_photo_in_multiple_stacks(self): method test_delete_photo_in_multiple_duplicate_groups (line 386) | def test_delete_photo_in_multiple_duplicate_groups(self): method test_delete_primary_photo_from_stack (line 410) | def test_delete_primary_photo_from_stack(self): class MetadataEdgeCasesTestCase (line 430) | class MetadataEdgeCasesTestCase(TestCase): method setUp (line 433) | def setUp(self): method test_photo_with_extreme_dimensions (line 436) | def test_photo_with_extreme_dimensions(self): method test_photo_with_zero_dimensions (line 449) | def test_photo_with_zero_dimensions(self): method test_duplicate_savings_with_zero_size (line 461) | def test_duplicate_savings_with_zero_size(self): FILE: api/tests/test_edit_photo_details.py class EditPhotoDetailsTest (line 9) | class EditPhotoDetailsTest(TestCase): method setUp (line 10) | def setUp(self): method test_should_update_timestamp (line 17) | def test_should_update_timestamp(self, extract_date_time_from_exif_mock): method test_should_not_update_other_properties (line 41) | def test_should_not_update_other_properties(self, extract_date_time_fr... FILE: api/tests/test_face_extractor.py class FaceExtractorTest (line 10) | class FaceExtractorTest(TestCase): method test_extract_from_dlib_handles_exception (line 14) | def test_extract_from_dlib_handles_exception(self, mock_get_face_locat... method test_extract_from_dlib_success (line 34) | def test_extract_from_dlib_success(self, mock_get_face_locations): method test_extract_prefers_exif (line 57) | def test_extract_prefers_exif(self, mock_dlib, mock_exif): method test_extract_fallback_to_dlib (line 78) | def test_extract_fallback_to_dlib(self, mock_dlib, mock_exif): FILE: api/tests/test_face_writeback.py class TestThumbnailCoordsToNormalized (line 20) | class TestThumbnailCoordsToNormalized(TestCase): method test_basic_conversion (line 21) | def test_basic_conversion(self): method test_corner_face (line 38) | def test_corner_face(self): class TestReverseOrientationTransform (line 54) | class TestReverseOrientationTransform(TestCase): method test_identity_for_normal_orientation (line 55) | def test_identity_for_normal_orientation(self): method test_identity_for_none_orientation (line 65) | def test_identity_for_none_orientation(self): method test_round_trip_rotate_90_cw (line 73) | def test_round_trip_rotate_90_cw(self): method test_round_trip_mirror_horizontal (line 77) | def test_round_trip_mirror_horizontal(self): method test_round_trip_rotate_180 (line 80) | def test_round_trip_rotate_180(self): method test_round_trip_mirror_vertical (line 83) | def test_round_trip_mirror_vertical(self): method test_round_trip_rotate_270_cw (line 86) | def test_round_trip_rotate_270_cw(self): method test_round_trip_mirror_horizontal_rotate_90_cw (line 89) | def test_round_trip_mirror_horizontal_rotate_90_cw(self): method _test_round_trip (line 92) | def _test_round_trip(self, orientation): class TestBuildFaceRegionExiftoolArgs (line 145) | class TestBuildFaceRegionExiftoolArgs(TestCase): method test_single_face (line 146) | def test_single_face(self): method test_multiple_faces (line 157) | def test_multiple_faces(self): method test_special_characters_in_name (line 170) | def test_special_characters_in_name(self): method test_escape_braces_and_equals (line 178) | def test_escape_braces_and_equals(self): method test_applied_to_dimensions (line 185) | def test_applied_to_dimensions(self): method test_no_applied_to_dimensions_when_missing (line 192) | def test_no_applied_to_dimensions_when_missing(self): method test_subject_keywords_for_named_faces (line 199) | def test_subject_keywords_for_named_faces(self): method test_no_subject_keywords_when_all_unnamed (line 210) | def test_no_subject_keywords_when_all_unnamed(self): class TestRoundTripCoordinates (line 219) | class TestRoundTripCoordinates(TestCase): method test_round_trip_no_orientation (line 220) | def test_round_trip_no_orientation(self): class TestGetFaceRegionTags (line 251) | class TestGetFaceRegionTags(TestCase): method setUp (line 252) | def setUp(self): method test_returns_tags_for_labeled_faces (line 257) | def test_returns_tags_for_labeled_faces(self, mock_pil_open, mock_get_... method test_returns_all_faces (line 294) | def test_returns_all_faces(self, mock_pil_open, mock_get_metadata): method test_unlabeled_faces_written_with_empty_name (line 326) | def test_unlabeled_faces_written_with_empty_name( method test_faces_with_no_person_written_with_empty_name (line 360) | def test_faces_with_no_person_written_with_empty_name( method test_mixed_labeled_and_unlabeled_faces (line 389) | def test_mixed_labeled_and_unlabeled_faces( method test_skips_deleted_faces (line 434) | def test_skips_deleted_faces(self, mock_pil_open, mock_get_metadata): class TestSaveMetadataIntegration (line 475) | class TestSaveMetadataIntegration(TestCase): method setUp (line 476) | def setUp(self): method test_save_metadata_with_face_tags (line 482) | def test_save_metadata_with_face_tags( method test_save_metadata_default_does_not_write_face_tags (line 514) | def test_save_metadata_default_does_not_write_face_tags(self, mock_wri... method test_save_metadata_combined_types (line 541) | def test_save_metadata_combined_types( FILE: api/tests/test_favorite_photos.py class FavoritePhotosTest (line 9) | class FavoritePhotosTest(TestCase): method setUp (line 10) | def setUp(self): method test_tag_my_photos_as_favorite (line 16) | def test_tag_my_photos_as_favorite(self): method test_untag_my_photos_as_favorite (line 32) | def test_untag_my_photos_as_favorite(self): method test_tag_photos_of_other_user_as_favorite (line 51) | def test_tag_photos_of_other_user_as_favorite(self): method test_tag_nonexistent_photo_as_favorite (line 69) | def test_tag_nonexistent_photo_as_favorite(self, logger): FILE: api/tests/test_file_model.py function _ensure_stub_modules (line 9) | def _ensure_stub_modules(): function _load_file_module (line 132) | def _load_file_module(): class TestIsVideo (line 147) | class TestIsVideo(unittest.TestCase): method setUpClass (line 149) | def setUpClass(cls): method test_is_video_returns_false_when_magic_raises (line 157) | def test_is_video_returns_false_when_magic_raises(self): FILE: api/tests/test_file_path_uniqueness.py class FilePathUniqueConstraintTestCase (line 22) | class FilePathUniqueConstraintTestCase(TestCase): method setUp (line 25) | def setUp(self): method test_unique_constraint_prevents_duplicate_paths (line 28) | def test_unique_constraint_prevents_duplicate_paths(self): method test_unique_constraint_allows_different_paths (line 48) | def test_unique_constraint_allows_different_paths(self): method test_empty_paths_are_unique (line 64) | def test_empty_paths_are_unique(self): class FileCreateMethodTestCase (line 83) | class FileCreateMethodTestCase(TestCase): method setUp (line 86) | def setUp(self): method tearDown (line 91) | def tearDown(self): method _create_test_file (line 96) | def _create_test_file(self, filename, content=b"test content"): method test_create_returns_existing_file_for_same_path (line 103) | def test_create_returns_existing_file_for_same_path(self): method test_create_creates_new_file_for_different_path (line 120) | def test_create_creates_new_file_for_different_path(self): method test_create_returns_existing_even_if_content_changed (line 132) | def test_create_returns_existing_even_if_content_changed(self): method test_create_determines_correct_file_type (line 151) | def test_create_determines_correct_file_type(self): class MigrationDeduplicationTestCase (line 169) | class MigrationDeduplicationTestCase(TestCase): method setUp (line 172) | def setUp(self): method test_deduplication_prefers_non_missing_file (line 175) | def test_deduplication_prefers_non_missing_file(self): method test_deduplication_keeps_file_with_more_photos (line 204) | def test_deduplication_keeps_file_with_more_photos(self): class ConcurrentScanTestCase (line 236) | class ConcurrentScanTestCase(TransactionTestCase): method setUp (line 239) | def setUp(self): method tearDown (line 243) | def tearDown(self): method _create_test_file (line 247) | def _create_test_file(self, filename, content=b"test content"): method test_concurrent_create_same_path_no_duplicates (line 254) | def test_concurrent_create_same_path_no_duplicates(self): method test_concurrent_create_different_paths_succeeds (line 289) | def test_concurrent_create_different_paths_succeeds(self): class FilePathLookupTestCase (line 317) | class FilePathLookupTestCase(TestCase): method setUp (line 320) | def setUp(self): method test_filter_by_path_is_exact (line 323) | def test_filter_by_path_is_exact(self): method test_photo_files_path_lookup (line 341) | def test_photo_files_path_lookup(self): class PhotoFileAssociationTestCase (line 362) | class PhotoFileAssociationTestCase(TestCase): method setUp (line 365) | def setUp(self): method test_multiple_photos_can_share_same_file (line 368) | def test_multiple_photos_can_share_same_file(self): method test_photo_with_multiple_file_variants (line 391) | def test_photo_with_multiple_file_variants(self): FILE: api/tests/test_geocode.py class MapboxLocation (line 33) | class MapboxLocation: method __init__ (line 34) | def __init__(self, raw): class TomTomLocation (line 39) | class TomTomLocation: method __init__ (line 40) | def __init__(self, raw): class NominatimLocation (line 45) | class NominatimLocation: method __init__ (line 46) | def __init__(self, raw): class OpenCageLocation (line 51) | class OpenCageLocation: method __init__ (line 52) | def __init__(self, raw): class TestGeocodeParsers (line 57) | class TestGeocodeParsers(TestCase): method test_mapbox_parser (line 58) | def test_mapbox_parser(self): method test_tomtom_parser (line 64) | def test_tomtom_parser(self): method test_nominatim_parser (line 70) | def test_nominatim_parser(self): method test_opencage_parser (line 76) | def test_opencage_parser(self): class FakeLocation (line 83) | class FakeLocation: method __init__ (line 87) | def __init__(self, location): class FakeProvider (line 92) | class FakeProvider: method __init__ (line 93) | def __init__(self, response): method reverse (line 96) | def reverse(self, _): function fake_geocoder (line 100) | def fake_geocoder(response): class TestGeocoder (line 104) | class TestGeocoder(TestCase): method test_reverse_geocode (line 107) | def test_reverse_geocode(self, get_geocoder_for_service_mock): method test_reverse_geocode_no_api_key (line 114) | def test_reverse_geocode_no_api_key(self): FILE: api/tests/test_get_faces.py class IncompleteFacesTest (line 13) | class IncompleteFacesTest(TestCase): method setUp (line 14) | def setUp(self): method test_if_classification_person_is_ignored_if_below_threshold (line 20) | def test_if_classification_person_is_ignored_if_below_threshold(self): method test_if_min_confidence_and_prob_are_compared_correctly (line 44) | def test_if_min_confidence_and_prob_are_compared_correctly( method test_incomplete_faces_with_clustering (line 77) | def test_incomplete_faces_with_clustering(self): method test_no_inferred_faces (line 108) | def test_no_inferred_faces(self): class FaceListViewTest (line 123) | class FaceListViewTest(TestCase): method setUp (line 124) | def setUp(self): method test_min_confidence_when_classification (line 130) | def test_min_confidence_when_classification(self): method test_min_confidence_but_for_unknown_other (line 156) | def test_min_confidence_but_for_unknown_other(self): method test_min_confidence_when_clustering (line 178) | def test_min_confidence_when_clustering(self): method test_min_confidence_when_clustering_and_unknown (line 200) | def test_min_confidence_when_clustering_and_unknown(self): method test_face_list_classification_order_by_probability (line 220) | def test_face_list_classification_order_by_probability(self): method test_face_list_clustering_order_by_probability (line 249) | def test_face_list_clustering_order_by_probability(self): method test_face_list_order_by_date (line 274) | def test_face_list_order_by_date(self): FILE: api/tests/test_hide_photos.py class FavoritePhotosTest (line 9) | class FavoritePhotosTest(TestCase): method setUp (line 10) | def setUp(self): method test_hide_my_photos (line 16) | def test_hide_my_photos(self): method test_untag_my_photos_as_favorite (line 32) | def test_untag_my_photos_as_favorite(self): method test_tag_photos_of_other_user_as_favorite (line 49) | def test_tag_photos_of_other_user_as_favorite(self): method test_tag_nonexistent_photo_as_favorite (line 67) | def test_tag_nonexistent_photo_as_favorite(self, logger): FILE: api/tests/test_im2txt.py function test_coco (line 18) | def test_coco(testcase, device="cpu", model="im2txt"): class Im2TxtBenchmark (line 90) | class Im2TxtBenchmark(TestCase): method setUp (line 101) | def setUp(self) -> None: method tearDown (line 139) | def tearDown(self) -> None: method monitor_ram_usage (line 161) | def monitor_ram_usage(self, process, ram_usages, duration): method test_im2txt_cpu (line 170) | def test_im2txt_cpu(self): method test_im2txt_gpu (line 182) | def test_im2txt_gpu(self): method test_im2txt_cpu_100 (line 193) | def test_im2txt_cpu_100(self): method test_im2txt_gpu_100 (line 205) | def test_im2txt_gpu_100(self): method test_im2txt_coco_cpu (line 216) | def test_im2txt_coco_cpu(self): method test_im2txt_coco_gpu (line 220) | def test_im2txt_coco_gpu(self): method test_blip_coco_cpu (line 224) | def test_blip_coco_cpu(self): method test_blip_coco_gpu (line 228) | def test_blip_coco_gpu(self): FILE: api/tests/test_live_photo.py class LocateGoogleEmbeddedVideoTestCase (line 40) | class LocateGoogleEmbeddedVideoTestCase(TestCase): method test_finds_ftypmp42_signature (line 43) | def test_finds_ftypmp42_signature(self): method test_finds_ftypisom_signature (line 51) | def test_finds_ftypisom_signature(self): method test_finds_ftypiso2_signature (line 58) | def test_finds_ftypiso2_signature(self): method test_returns_minus_one_when_not_found (line 65) | def test_returns_minus_one_when_not_found(self): method test_empty_data (line 71) | def test_empty_data(self): method test_finds_first_signature_if_multiple (line 76) | def test_finds_first_signature_if_multiple(self): class LocateSamsungEmbeddedVideoTestCase (line 84) | class LocateSamsungEmbeddedVideoTestCase(TestCase): method test_finds_samsung_marker (line 87) | def test_finds_samsung_marker(self): method test_returns_minus_one_when_not_found (line 94) | def test_returns_minus_one_when_not_found(self): method test_empty_data (line 100) | def test_empty_data(self): class HasEmbeddedMotionVideoTestCase (line 106) | class HasEmbeddedMotionVideoTestCase(TestCase): method setUp (line 109) | def setUp(self): method tearDown (line 113) | def tearDown(self): method test_returns_false_for_non_jpeg (line 119) | def test_returns_false_for_non_jpeg(self, mock_magic_class): method test_returns_true_for_google_motion_photo (line 131) | def test_returns_true_for_google_motion_photo(self, mock_mmap, mock_op... method test_returns_false_on_exception (line 154) | def test_returns_false_on_exception(self, mock_magic_class): class FindAppleLivePhotoVideoTestCase (line 164) | class FindAppleLivePhotoVideoTestCase(TestCase): method setUp (line 167) | def setUp(self): method tearDown (line 171) | def tearDown(self): method test_finds_lowercase_mov_companion (line 176) | def test_finds_lowercase_mov_companion(self): method test_finds_uppercase_mov_companion (line 187) | def test_finds_uppercase_mov_companion(self): method test_returns_none_when_no_companion (line 197) | def test_returns_none_when_no_companion(self): method test_prefers_lowercase_mov (line 205) | def test_prefers_lowercase_mov(self): method test_handles_different_image_extensions (line 218) | def test_handles_different_image_extensions(self): class ExtractEmbeddedMotionVideoTestCase (line 234) | class ExtractEmbeddedMotionVideoTestCase(TestCase): method setUp (line 237) | def setUp(self): method tearDown (line 241) | def tearDown(self): method test_extracts_google_motion_video (line 247) | def test_extracts_google_motion_video(self): method test_returns_none_for_no_embedded_video (line 275) | def test_returns_none_for_no_embedded_video(self): method test_returns_none_on_file_error (line 286) | def test_returns_none_on_file_error(self): class DetectLivePhotoTestCase (line 294) | class DetectLivePhotoTestCase(TestCase): method setUp (line 297) | def setUp(self): method tearDown (line 302) | def tearDown(self): method test_returns_none_for_photo_without_main_file (line 307) | def test_returns_none_for_photo_without_main_file(self): method test_detects_embedded_motion_video (line 317) | def test_detects_embedded_motion_video(self, mock_create, mock_has_emb... method test_detects_apple_live_photo (line 334) | def test_detects_apple_live_photo(self, mock_create, mock_find, mock_h... method test_returns_none_for_regular_photo (line 351) | def test_returns_none_for_regular_photo(self, mock_find, mock_has_embe... class CreateEmbeddedLivePhotoStackTestCase (line 363) | class CreateEmbeddedLivePhotoStackTestCase(TestCase): method setUp (line 366) | def setUp(self): method tearDown (line 371) | def tearDown(self): method test_returns_none_if_feature_disabled (line 377) | def test_returns_none_if_feature_disabled(self): method test_returns_none_if_extraction_fails (line 388) | def test_returns_none_if_extraction_fails(self, mock_extract): method test_returns_existing_stack_if_present (line 402) | def test_returns_existing_stack_if_present(self, mock_file_create, moc... class CreateAppleLivePhotoStackTestCase (line 420) | class CreateAppleLivePhotoStackTestCase(TestCase): method setUp (line 423) | def setUp(self): method tearDown (line 449) | def tearDown(self): method test_creates_new_stack_for_apple_live_photo (line 454) | def test_creates_new_stack_for_apple_live_photo(self): method test_returns_existing_stack_if_present (line 478) | def test_returns_existing_stack_if_present(self): class ProcessLivePhotosBatchTestCase (line 507) | class ProcessLivePhotosBatchTestCase(TestCase): method setUp (line 510) | def setUp(self): method test_processes_all_photos (line 515) | def test_processes_all_photos(self, mock_detect): method test_counts_detected_live_photos (line 527) | def test_counts_detected_live_photos(self, mock_detect): method test_counts_new_stacks_created (line 539) | def test_counts_new_stacks_created(self, mock_detect): method test_handles_exceptions_gracefully (line 552) | def test_handles_exceptions_gracefully(self, mock_detect): method test_empty_list_returns_zero_counts (line 565) | def test_empty_list_returns_zero_counts(self): class ConstantsTestCase (line 572) | class ConstantsTestCase(TestCase): method test_jpeg_eoi_marker (line 575) | def test_jpeg_eoi_marker(self): method test_google_signatures_list (line 579) | def test_google_signatures_list(self): method test_samsung_marker (line 585) | def test_samsung_marker(self): method test_apple_extensions (line 589) | def test_apple_extensions(self): class EdgeCasesTestCase (line 595) | class EdgeCasesTestCase(TestCase): method setUp (line 598) | def setUp(self): method tearDown (line 602) | def tearDown(self): method test_unicode_filename_apple_live_photo (line 607) | def test_unicode_filename_apple_live_photo(self): method test_special_characters_in_path (line 617) | def test_special_characters_in_path(self): method test_locate_video_at_start_of_data (line 627) | def test_locate_video_at_start_of_data(self): method test_multiple_samsung_markers (line 633) | def test_multiple_samsung_markers(self): method test_partial_signature_not_matched (line 642) | def test_partial_signature_not_matched(self): method test_very_large_file_simulation (line 649) | def test_very_large_file_simulation(self): method test_binary_data_with_nulls (line 658) | def test_binary_data_with_nulls(self): FILE: api/tests/test_location_timeline.py function prepare_database (line 12) | def prepare_database(user): class LocationTimelineTest (line 97) | class LocationTimelineTest(TestCase): method setUp (line 98) | def setUp(self) -> None: method test_location_timeline_endpoint (line 105) | def test_location_timeline_endpoint(self): method test_get_location_timeline (line 110) | def test_get_location_timeline(self): method test_get_photo_month_counts_endpoint (line 114) | def test_get_photo_month_counts_endpoint(self): method test_get_photo_month_count (line 119) | def test_get_photo_month_count(self): FILE: api/tests/test_metadata_ordering_sentinel.py function create_unique_png (line 13) | def create_unique_png(seed=0): class DummyAsyncTask (line 53) | class DummyAsyncTask: method __init__ (line 62) | def __init__(self, func, *args, **kwargs): method run (line 69) | def run(self): class DummyChain (line 83) | class DummyChain: method __init__ (line 84) | def __init__(self, *args, **kwargs): method append (line 87) | def append(self, *args, **kwargs): method run (line 91) | def run(self): class MetadataOrderingSentinelTest (line 95) | class MetadataOrderingSentinelTest(TestCase): method test_random_order_images_and_xmp_are_consistently_linked (line 96) | def test_random_order_images_and_xmp_are_consistently_linked(self): FILE: api/tests/test_migration_0099.py function _sqlite_table_exists (line 35) | def _sqlite_table_exists(cursor, table_name): function _sqlite_column_names (line 43) | def _sqlite_column_names(cursor, table_name): function _sqlite_pk_columns (line 48) | def _sqlite_pk_columns(cursor, table_name): function _sqlite_index_exists (line 53) | def _sqlite_index_exists(cursor, index_name): function _build_test_db (line 170) | def _build_test_db(): function _run_migration_on (line 223) | def _run_migration_on(sqlite_conn): class TestSQLiteMigration0099 (line 260) | class TestSQLiteMigration0099(TestCase): method setUpClass (line 269) | def setUpClass(cls): method tearDownClass (line 275) | def tearDownClass(cls): method _cursor (line 279) | def _cursor(self): method test_photo_has_id_column (line 284) | def test_photo_has_id_column(self): method test_photo_has_image_hash_column (line 289) | def test_photo_has_image_hash_column(self): method test_photo_pk_is_id (line 294) | def test_photo_pk_is_id(self): method test_image_hash_unique_index (line 299) | def test_image_hash_unique_index(self): method test_performance_indexes (line 303) | def test_performance_indexes(self): method test_all_photos_have_valid_uuids (line 321) | def test_all_photos_have_valid_uuids(self): method test_image_hashes_preserved (line 329) | def test_image_hashes_preserved(self): method test_each_photo_has_distinct_uuid (line 335) | def test_each_photo_has_distinct_uuid(self): method test_face_fk_translated (line 341) | def test_face_fk_translated(self): method test_no_orphan_faces (line 354) | def test_no_orphan_faces(self): method test_thumbnail_fk_translated (line 363) | def test_thumbnail_fk_translated(self): method test_photo_caption_fk_translated (line 373) | def test_photo_caption_fk_translated(self): method test_photo_search_fk_translated (line 382) | def test_photo_search_fk_translated(self): method test_person_cover_photo_translated (line 391) | def test_person_cover_photo_translated(self): method test_albumuser_cover_photo_translated (line 401) | def test_albumuser_cover_photo_translated(self): method test_m2m_shared_to_translated (line 410) | def test_m2m_shared_to_translated(self): method test_m2m_albumuser_photos_translated (line 418) | def test_m2m_albumuser_photos_translated(self): method test_m2m_albumthing_photos_translated (line 426) | def test_m2m_albumthing_photos_translated(self): method test_m2m_albumplace_photos_translated (line 434) | def test_m2m_albumplace_photos_translated(self): method test_m2m_albumdate_photos_translated (line 442) | def test_m2m_albumdate_photos_translated(self): method test_m2m_albumauto_photos_translated (line 450) | def test_m2m_albumauto_photos_translated(self): method test_albumthing_cover_photos_translated (line 458) | def test_albumthing_cover_photos_translated(self): method test_photostack_primary_photo_translated (line 466) | def test_photostack_primary_photo_translated(self): class TestPostMigrationSchema (line 481) | class TestPostMigrationSchema(TestCase): method test_photo_table_has_id_and_image_hash (line 490) | def test_photo_table_has_id_and_image_hash(self): method test_photo_pk_is_uuid_field (line 504) | def test_photo_pk_is_uuid_field(self): class TestMigrationDispatch (line 516) | class TestMigrationDispatch(TestCase): method test_dispatches_to_sqlite (line 519) | def test_dispatches_to_sqlite(self): method test_dispatches_to_postgresql (line 526) | def test_dispatches_to_postgresql(self): method test_rejects_unknown_backend (line 533) | def test_rejects_unknown_backend(self): method test_reverse_raises_runtime_error (line 539) | def test_reverse_raises_runtime_error(self): class TestSQLiteHelpers (line 548) | class TestSQLiteHelpers(TestCase): method test_column_info (line 551) | def test_column_info(self): method test_index_info (line 563) | def test_index_info(self): method test_recreate_table_changes_pk (line 573) | def test_recreate_table_changes_pk(self): method test_update_fk_table_translates_values (line 596) | def test_update_fk_table_translates_values(self): method test_update_fk_table_skips_missing_table (line 615) | def test_update_fk_table_skips_missing_table(self): FILE: api/tests/test_migration_0101.py class Migration0101TestCase (line 23) | class Migration0101TestCase(TestCase): method setUp (line 26) | def setUp(self): method test_subquery_with_uuid_primary_key (line 30) | def test_subquery_with_uuid_primary_key(self): method test_migration_logic_creates_metadata (line 65) | def test_migration_logic_creates_metadata(self): method test_batch_processing_without_iterator (line 122) | def test_batch_processing_without_iterator(self): method test_batch_processing_is_idempotent (line 205) | def test_batch_processing_is_idempotent(self): method test_bulk_create_ignore_conflicts_on_duplicate (line 245) | def test_bulk_create_ignore_conflicts_on_duplicate(self): FILE: api/tests/test_multi_user_isolation.py class DuplicateUserIsolationTestCase (line 19) | class DuplicateUserIsolationTestCase(APITestCase): method setUp (line 22) | def setUp(self): method test_user_cannot_see_other_user_duplicates (line 31) | def test_user_cannot_see_other_user_duplicates(self): method test_user_cannot_access_other_user_duplicate_detail (line 52) | def test_user_cannot_access_other_user_duplicate_detail(self): method test_user_cannot_resolve_other_user_duplicate (line 66) | def test_user_cannot_resolve_other_user_duplicate(self): method test_user_cannot_delete_other_user_duplicate (line 84) | def test_user_cannot_delete_other_user_duplicate(self): method test_admin_can_see_duplicate_stats (line 101) | def test_admin_can_see_duplicate_stats(self): class StackUserIsolationTestCase (line 118) | class StackUserIsolationTestCase(APITestCase): method setUp (line 121) | def setUp(self): method test_user_cannot_see_other_user_stacks (line 126) | def test_user_cannot_see_other_user_stacks(self): method test_user_cannot_access_other_user_stack_detail (line 144) | def test_user_cannot_access_other_user_stack_detail(self): method test_user_cannot_modify_other_user_stack (line 158) | def test_user_cannot_modify_other_user_stack(self): method test_user_cannot_delete_other_user_stack (line 177) | def test_user_cannot_delete_other_user_stack(self): method test_user_cannot_create_stack_with_other_user_photos (line 194) | def test_user_cannot_create_stack_with_other_user_photos(self): class SharedPhotoIsolationTestCase (line 216) | class SharedPhotoIsolationTestCase(TestCase): method setUp (line 219) | def setUp(self): method test_shared_photo_not_in_receiver_stacks (line 223) | def test_shared_photo_not_in_receiver_stacks(self): method test_shared_photo_not_in_receiver_duplicates (line 243) | def test_shared_photo_not_in_receiver_duplicates(self): method test_user_stack_unaffected_by_shared_photos (line 262) | def test_user_stack_unaffected_by_shared_photos(self): class DetectionUserIsolationTestCase (line 280) | class DetectionUserIsolationTestCase(APITestCase): method setUp (line 283) | def setUp(self): method test_duplicate_detection_only_affects_own_photos (line 288) | def test_duplicate_detection_only_affects_own_photos(self): method test_stack_detection_only_affects_own_photos (line 314) | def test_stack_detection_only_affects_own_photos(self): class CrossUserOperationTestCase (line 332) | class CrossUserOperationTestCase(APITestCase): method setUp (line 335) | def setUp(self): method test_cannot_add_other_user_photo_to_own_stack (line 340) | def test_cannot_add_other_user_photo_to_own_stack(self): method test_cannot_resolve_duplicate_with_other_user_photo (line 363) | def test_cannot_resolve_duplicate_with_other_user_photo(self): method test_cannot_set_other_user_photo_as_stack_primary (line 386) | def test_cannot_set_other_user_photo_as_stack_primary(self): class AdminAccessTestCase (line 410) | class AdminAccessTestCase(APITestCase): method setUp (line 413) | def setUp(self): method test_admin_can_view_stats_for_all_users (line 420) | def test_admin_can_view_stats_for_all_users(self): method test_regular_user_sees_only_own_stats (line 435) | def test_regular_user_sees_only_own_stats(self): FILE: api/tests/test_only_photos_or_only_videos.py class OnlyPhotosOrOnlyVideosTest (line 9) | class OnlyPhotosOrOnlyVideosTest(TestCase): method setUp (line 10) | def setUp(self): method test_only_photos (line 15) | def test_only_photos(self): FILE: api/tests/test_perceptual_hash.py class HammingDistanceTestCase (line 29) | class HammingDistanceTestCase(TestCase): method test_identical_hashes_return_zero (line 32) | def test_identical_hashes_return_zero(self): method test_completely_different_hashes (line 37) | def test_completely_different_hashes(self): method test_one_bit_difference (line 45) | def test_one_bit_difference(self): method test_half_bits_different (line 53) | def test_half_bits_different(self): method test_invalid_hash_returns_max_distance (line 61) | def test_invalid_hash_returns_max_distance(self): method test_empty_strings_return_max_distance (line 66) | def test_empty_strings_return_max_distance(self): method test_mixed_valid_invalid_returns_max_distance (line 71) | def test_mixed_valid_invalid_returns_max_distance(self): method test_different_length_hashes (line 77) | def test_different_length_hashes(self): method test_real_phash_values (line 85) | def test_real_phash_values(self): method test_case_insensitive_hashes (line 93) | def test_case_insensitive_hashes(self): class AreDuplicatesTestCase (line 101) | class AreDuplicatesTestCase(TestCase): method test_identical_hashes_are_duplicates (line 104) | def test_identical_hashes_are_duplicates(self): method test_distance_under_threshold_is_duplicate (line 109) | def test_distance_under_threshold_is_duplicate(self): method test_distance_at_threshold_is_duplicate (line 116) | def test_distance_at_threshold_is_duplicate(self): method test_distance_over_threshold_not_duplicate (line 121) | def test_distance_over_threshold_not_duplicate(self): method test_empty_hash1_not_duplicate (line 126) | def test_empty_hash1_not_duplicate(self): method test_empty_hash2_not_duplicate (line 130) | def test_empty_hash2_not_duplicate(self): method test_none_hash1_not_duplicate (line 134) | def test_none_hash1_not_duplicate(self): method test_none_hash2_not_duplicate (line 138) | def test_none_hash2_not_duplicate(self): method test_both_none_not_duplicate (line 142) | def test_both_none_not_duplicate(self): method test_both_empty_not_duplicate (line 146) | def test_both_empty_not_duplicate(self): method test_custom_threshold_strict (line 150) | def test_custom_threshold_strict(self): method test_custom_threshold_loose (line 156) | def test_custom_threshold_loose(self): method test_default_threshold_value (line 162) | def test_default_threshold_value(self): class FindSimilarHashesTestCase (line 167) | class FindSimilarHashesTestCase(TestCase): method test_empty_target_hash_returns_empty (line 170) | def test_empty_target_hash_returns_empty(self): method test_none_target_hash_returns_empty (line 176) | def test_none_target_hash_returns_empty(self): method test_empty_hash_list_returns_empty (line 182) | def test_empty_hash_list_returns_empty(self): method test_finds_similar_hashes (line 187) | def test_finds_similar_hashes(self): method test_excludes_distant_hashes (line 198) | def test_excludes_distant_hashes(self): method test_skips_identical_hash (line 206) | def test_skips_identical_hash(self): method test_skips_none_hash_in_list (line 213) | def test_skips_none_hash_in_list(self): method test_skips_empty_hash_in_list (line 222) | def test_skips_empty_hash_in_list(self): method test_sorted_by_distance (line 231) | def test_sorted_by_distance(self): method test_custom_threshold (line 244) | def test_custom_threshold(self): method test_returns_correct_tuple_format (line 257) | def test_returns_correct_tuple_format(self): method test_multiple_similar_all_returned (line 268) | def test_multiple_similar_all_returned(self): class CalculatePerceptualHashTestCase (line 277) | class CalculatePerceptualHashTestCase(TestCase): method setUp (line 280) | def setUp(self): method tearDown (line 284) | def tearDown(self): method _create_test_image (line 290) | def _create_test_image(self, filename, size=(100, 100), mode="RGB", co... method test_valid_rgb_image (line 297) | def test_valid_rgb_image(self): method test_valid_rgba_image_converted (line 305) | def test_valid_rgba_image_converted(self): method test_valid_grayscale_image (line 312) | def test_valid_grayscale_image(self): method test_valid_palette_image_converted (line 319) | def test_valid_palette_image_converted(self): method test_nonexistent_file_returns_none (line 327) | def test_nonexistent_file_returns_none(self): method test_corrupted_file_returns_none (line 332) | def test_corrupted_file_returns_none(self): method test_empty_file_returns_none (line 340) | def test_empty_file_returns_none(self): method test_directory_instead_of_file_returns_none (line 348) | def test_directory_instead_of_file_returns_none(self): method test_custom_hash_size (line 353) | def test_custom_hash_size(self): method test_small_hash_size (line 361) | def test_small_hash_size(self): method test_similar_images_similar_hashes (line 369) | def test_similar_images_similar_hashes(self): method test_different_images_different_hashes (line 382) | def test_different_images_different_hashes(self): method test_deterministic_hash (line 411) | def test_deterministic_hash(self): method test_very_small_image (line 418) | def test_very_small_image(self): method test_very_large_image (line 425) | def test_very_large_image(self): method test_jpeg_vs_png_same_content (line 432) | def test_jpeg_vs_png_same_content(self): class CalculateHashFromThumbnailTestCase (line 446) | class CalculateHashFromThumbnailTestCase(TestCase): method setUp (line 449) | def setUp(self): method tearDown (line 453) | def tearDown(self): method test_delegates_to_calculate_perceptual_hash (line 459) | def test_delegates_to_calculate_perceptual_hash(self): method test_returns_none_on_failure (line 467) | def test_returns_none_on_failure(self): class EdgeCasesTestCase (line 473) | class EdgeCasesTestCase(TestCase): method setUp (line 476) | def setUp(self): method tearDown (line 480) | def tearDown(self): method test_unicode_filename (line 486) | def test_unicode_filename(self): method test_special_characters_in_path (line 494) | def test_special_characters_in_path(self): method test_hash_only_contains_hex_chars (line 502) | def test_hash_only_contains_hex_chars(self): method test_webp_format (line 513) | def test_webp_format(self): method test_gif_format (line 521) | def test_gif_format(self): method test_bmp_format (line 529) | def test_bmp_format(self): method test_hamming_distance_with_newlines_in_hash (line 537) | def test_hamming_distance_with_newlines_in_hash(self): method test_find_similar_with_large_list (line 545) | def test_find_similar_with_large_list(self): method test_are_duplicates_with_whitespace_only_hash (line 554) | def test_are_duplicates_with_whitespace_only_hash(self): method test_cmyk_image_converted (line 559) | def test_cmyk_image_converted(self): method test_1bit_image (line 568) | def test_1bit_image(self): method test_concurrent_hash_calculation (line 576) | def test_concurrent_hash_calculation(self): class PerformanceTestCase (line 614) | class PerformanceTestCase(TestCase): method setUp (line 617) | def setUp(self): method tearDown (line 621) | def tearDown(self): method test_hamming_distance_performance (line 627) | def test_hamming_distance_performance(self): method test_find_similar_performance (line 642) | def test_find_similar_performance(self): FILE: api/tests/test_photo_caption_model.py class PhotoCaptionModelTest (line 7) | class PhotoCaptionModelTest(TestCase): method setUp (line 8) | def setUp(self): method test_create_photo_caption (line 12) | def test_create_photo_caption(self): method test_photo_caption_one_to_one_relationship (line 21) | def test_photo_caption_one_to_one_relationship(self): method test_generate_captions_im2txt (line 33) | def test_generate_captions_im2txt(self): method test_save_user_caption (line 42) | def test_save_user_caption(self): method test_generate_tag_captions_skips_existing (line 51) | def test_generate_tag_captions_skips_existing(self): method test_recreate_search_captions_delegates_to_photo_search (line 71) | def test_recreate_search_captions_delegates_to_photo_search(self): method test_captions_json_default_empty_dict (line 87) | def test_captions_json_default_empty_dict(self): method test_str_representation (line 93) | def test_str_representation(self): method test_cascade_delete_with_photo (line 102) | def test_cascade_delete_with_photo(self): method test_multiple_caption_types (line 112) | def test_multiple_caption_types(self): method test_update_existing_captions (line 131) | def test_update_existing_captions(self): method test_empty_captions_json_handling (line 144) | def test_empty_captions_json_handling(self): FILE: api/tests/test_photo_captions.py class PhotoCaptionsTest (line 9) | class PhotoCaptionsTest(TestCase): method setUp (line 10) | def setUp(self): method test_generate_captions_for_my_photo (line 19) | def test_generate_captions_for_my_photo(self, generate_caption_mock): method test_fail_to_generate_captions_for_my_photo (line 38) | def test_fail_to_generate_captions_for_my_photo(self, generate_caption... method test_generate_captions_for_my_photo_of_another_user (line 54) | def test_generate_captions_for_my_photo_of_another_user(self): FILE: api/tests/test_photo_lifecycle.py class PhotoDeletionStackCleanupTestCase (line 19) | class PhotoDeletionStackCleanupTestCase(TestCase): method setUp (line 22) | def setUp(self): method test_manual_delete_clears_stack_membership (line 25) | def test_manual_delete_clears_stack_membership(self): method test_manual_delete_deletes_stack_with_one_remaining (line 52) | def test_manual_delete_deletes_stack_with_one_remaining(self): method test_manual_delete_deletes_empty_stack (line 76) | def test_manual_delete_deletes_empty_stack(self): class PhotoDeletionDuplicateCleanupTestCase (line 101) | class PhotoDeletionDuplicateCleanupTestCase(TestCase): method setUp (line 104) | def setUp(self): method test_manual_delete_clears_duplicate_membership (line 107) | def test_manual_delete_clears_duplicate_membership(self): method test_manual_delete_deletes_duplicate_with_one_remaining (line 138) | def test_manual_delete_deletes_duplicate_with_one_remaining(self): method test_manual_delete_deletes_empty_duplicate_group (line 166) | def test_manual_delete_deletes_empty_duplicate_group(self): class PhotoTrashRestoreTestCase (line 192) | class PhotoTrashRestoreTestCase(TestCase): method setUp (line 195) | def setUp(self): method test_trashed_photo_preserves_stack_membership (line 198) | def test_trashed_photo_preserves_stack_membership(self): method test_trashed_photo_preserves_duplicate_membership (line 220) | def test_trashed_photo_preserves_duplicate_membership(self): method test_restore_photo_from_trash (line 242) | def test_restore_photo_from_trash(self): class PhotoInMultipleGroupsTestCase (line 254) | class PhotoInMultipleGroupsTestCase(TestCase): method setUp (line 257) | def setUp(self): method test_photo_in_both_stack_and_duplicate (line 260) | def test_photo_in_both_stack_and_duplicate(self): method test_photo_in_multiple_stacks (line 300) | def test_photo_in_multiple_stacks(self): class DuplicateResolutionCleanupTestCase (line 333) | class DuplicateResolutionCleanupTestCase(TestCase): method setUp (line 336) | def setUp(self): method test_resolve_duplicate_trashes_non_kept_photos (line 339) | def test_resolve_duplicate_trashes_non_kept_photos(self): method test_resolve_duplicate_updates_status (line 364) | def test_resolve_duplicate_updates_status(self): class EdgeCasesTestCase (line 382) | class EdgeCasesTestCase(TestCase): method setUp (line 385) | def setUp(self): method test_delete_photo_not_in_any_group (line 388) | def test_delete_photo_not_in_any_group(self): method test_delete_photo_with_no_main_file (line 399) | def test_delete_photo_with_no_main_file(self): method test_stack_primary_photo_deleted (line 414) | def test_stack_primary_photo_deleted(self): method test_duplicate_kept_photo_deleted (line 440) | def test_duplicate_kept_photo_deleted(self): class SharedFileTestCase (line 464) | class SharedFileTestCase(TestCase): method setUp (line 467) | def setUp(self): method test_delete_photo_preserves_shared_file (line 470) | def test_delete_photo_preserves_shared_file(self): method test_delete_photo_removes_unshared_file (line 512) | def test_delete_photo_removes_unshared_file(self): method test_delete_photo_with_shared_main_file_different_from_files (line 555) | def test_delete_photo_with_shared_main_file_different_from_files(self): method test_delete_last_photo_using_shared_file (line 598) | def test_delete_last_photo_using_shared_file(self): FILE: api/tests/test_photo_list_without_timestamp.py class PhotoListWithoutTimestampTest (line 8) | class PhotoListWithoutTimestampTest(TestCase): method setUp (line 9) | def setUp(self): method test_retrieve_photos_without_exif_timestamp (line 14) | def test_retrieve_photos_without_exif_timestamp(self): FILE: api/tests/test_photo_metadata.py class PhotoMetadataModelTestCase (line 24) | class PhotoMetadataModelTestCase(TestCase): method setUp (line 27) | def setUp(self): method test_create_metadata_basic (line 31) | def test_create_metadata_basic(self): method test_metadata_source_choices (line 47) | def test_metadata_source_choices(self): method test_resolution_property (line 54) | def test_resolution_property(self): method test_resolution_property_missing_dimensions (line 64) | def test_resolution_property_missing_dimensions(self): method test_megapixels_property (line 74) | def test_megapixels_property(self): method test_megapixels_property_missing_dimensions (line 85) | def test_megapixels_property_missing_dimensions(self): method test_has_location_property_with_gps (line 95) | def test_has_location_property_with_gps(self): method test_has_location_property_without_gps (line 105) | def test_has_location_property_without_gps(self): method test_has_location_partial_gps (line 113) | def test_has_location_partial_gps(self): method test_camera_display_make_and_model (line 123) | def test_camera_display_make_and_model(self): method test_camera_display_model_includes_make (line 133) | def test_camera_display_model_includes_make(self): method test_camera_display_only_model (line 144) | def test_camera_display_only_model(self): method test_lens_display (line 153) | def test_lens_display(self): method test_lens_display_model_includes_make (line 163) | def test_lens_display_model_includes_make(self): method test_version_increments (line 173) | def test_version_increments(self): method test_raw_data_json_fields (line 189) | def test_raw_data_json_fields(self): method test_keywords_json_field (line 204) | def test_keywords_json_field(self): class MetadataFileModelTestCase (line 217) | class MetadataFileModelTestCase(TestCase): method setUp (line 220) | def setUp(self): method test_file_type_choices (line 224) | def test_file_type_choices(self): method test_source_choices (line 231) | def test_source_choices(self): class MetadataEditModelTestCase (line 239) | class MetadataEditModelTestCase(TestCase): method setUp (line 242) | def setUp(self): method test_create_edit_record (line 246) | def test_create_edit_record(self): method test_edit_records_ordered_by_created_at (line 263) | def test_edit_records_ordered_by_created_at(self): class PhotoMetadataAPITestCase (line 286) | class PhotoMetadataAPITestCase(TestCase): method setUp (line 289) | def setUp(self): method test_retrieve_metadata (line 307) | def test_retrieve_metadata(self): method test_retrieve_metadata_by_image_hash (line 317) | def test_retrieve_metadata_by_image_hash(self): method test_retrieve_metadata_creates_if_missing (line 323) | def test_retrieve_metadata_creates_if_missing(self): method test_retrieve_metadata_other_user_forbidden (line 336) | def test_retrieve_metadata_other_user_forbidden(self): method test_update_metadata (line 344) | def test_update_metadata(self): method test_update_creates_edit_history (line 358) | def test_update_creates_edit_history(self): method test_get_edit_history (line 375) | def test_get_edit_history(self): method test_revert_edit (line 401) | def test_revert_edit(self): method test_revert_creates_new_edit_record (line 426) | def test_revert_creates_new_edit_record(self): method test_revert_nonexistent_edit (line 445) | def test_revert_nonexistent_edit(self): method test_unauthenticated_request (line 453) | def test_unauthenticated_request(self): class BulkMetadataAPITestCase (line 462) | class BulkMetadataAPITestCase(TestCase): method setUp (line 465) | def setUp(self): method test_bulk_get_metadata (line 485) | def test_bulk_get_metadata(self): method test_bulk_get_no_photo_ids (line 496) | def test_bulk_get_no_photo_ids(self): method test_bulk_get_max_100_photos (line 502) | def test_bulk_get_max_100_photos(self): method test_bulk_update_metadata (line 512) | def test_bulk_update_metadata(self): method test_bulk_update_no_photo_ids (line 532) | def test_bulk_update_no_photo_ids(self): method test_bulk_update_no_updates (line 542) | def test_bulk_update_no_updates(self): method test_bulk_update_invalid_fields (line 552) | def test_bulk_update_invalid_fields(self): method test_bulk_update_creates_edit_history (line 566) | def test_bulk_update_creates_edit_history(self): method test_bulk_update_other_user_photos_ignored (line 582) | def test_bulk_update_other_user_photos_ignored(self): class PhotoMetadataEdgeCasesTestCase (line 601) | class PhotoMetadataEdgeCasesTestCase(TestCase): method setUp (line 604) | def setUp(self): method test_metadata_with_special_characters (line 610) | def test_metadata_with_special_characters(self): method test_metadata_with_very_long_caption (line 623) | def test_metadata_with_very_long_caption(self): method test_metadata_with_null_values (line 634) | def test_metadata_with_null_values(self): method test_metadata_with_zero_values (line 648) | def test_metadata_with_zero_values(self): method test_metadata_with_negative_gps (line 661) | def test_metadata_with_negative_gps(self): method test_one_to_one_relationship_enforced (line 672) | def test_one_to_one_relationship_enforced(self): method test_invalid_uuid_in_url (line 679) | def test_invalid_uuid_in_url(self): method test_staff_can_access_any_photo_metadata (line 686) | def test_staff_can_access_any_photo_metadata(self): method test_update_increments_version (line 699) | def test_update_increments_version(self): method test_revert_all_records_action (line 717) | def test_revert_all_records_action(self): method test_keywords_array_update (line 739) | def test_keywords_array_update(self): method test_empty_keywords_update (line 757) | def test_empty_keywords_update(self): FILE: api/tests/test_photo_metadata_api.py class PhotoMetadataRetrieveTestCase (line 22) | class PhotoMetadataRetrieveTestCase(APITestCase): method setUp (line 25) | def setUp(self): method test_get_metadata_by_uuid (line 31) | def test_get_metadata_by_uuid(self): method test_get_metadata_by_image_hash (line 39) | def test_get_metadata_by_image_hash(self): method test_get_metadata_creates_if_missing (line 44) | def test_get_metadata_creates_if_missing(self): method test_get_metadata_nonexistent_photo (line 55) | def test_get_metadata_nonexistent_photo(self): method test_get_metadata_other_user_forbidden (line 61) | def test_get_metadata_other_user_forbidden(self): method test_get_metadata_admin_can_access_any (line 69) | def test_get_metadata_admin_can_access_any(self): class PhotoMetadataUpdateTestCase (line 82) | class PhotoMetadataUpdateTestCase(APITestCase): method setUp (line 85) | def setUp(self): method test_update_metadata_title (line 91) | def test_update_metadata_title(self): method test_update_metadata_creates_history (line 104) | def test_update_metadata_creates_history(self): method test_update_metadata_rating (line 126) | def test_update_metadata_rating(self): method test_update_metadata_caption (line 138) | def test_update_metadata_caption(self): method test_update_metadata_version_increments (line 150) | def test_update_metadata_version_increments(self): method test_update_metadata_forbidden_for_other_user (line 166) | def test_update_metadata_forbidden_for_other_user(self): class PhotoMetadataHistoryTestCase (line 179) | class PhotoMetadataHistoryTestCase(APITestCase): method setUp (line 182) | def setUp(self): method test_get_empty_history (line 188) | def test_get_empty_history(self): method test_get_history_with_edits (line 195) | def test_get_history_with_edits(self): method test_history_pagination (line 212) | def test_history_pagination(self): method test_history_ordered_by_date (line 232) | def test_history_ordered_by_date(self): class PhotoMetadataRevertTestCase (line 259) | class PhotoMetadataRevertTestCase(APITestCase): method setUp (line 262) | def setUp(self): method test_revert_single_edit (line 269) | def test_revert_single_edit(self): method test_revert_creates_history_entry (line 293) | def test_revert_creates_history_entry(self): method test_revert_nonexistent_edit (line 314) | def test_revert_nonexistent_edit(self): method test_revert_edit_from_wrong_photo (line 320) | def test_revert_edit_from_wrong_photo(self): class PhotoMetadataRevertAllTestCase (line 338) | class PhotoMetadataRevertAllTestCase(APITestCase): method setUp (line 341) | def setUp(self): method test_revert_all_creates_history (line 347) | def test_revert_all_creates_history(self): class BulkMetadataGetTestCase (line 362) | class BulkMetadataGetTestCase(APITestCase): method setUp (line 365) | def setUp(self): method test_bulk_get_by_uuid (line 371) | def test_bulk_get_by_uuid(self): method test_bulk_get_by_image_hash (line 378) | def test_bulk_get_by_image_hash(self): method test_bulk_get_mixed_ids (line 385) | def test_bulk_get_mixed_ids(self): method test_bulk_get_no_ids (line 392) | def test_bulk_get_no_ids(self): method test_bulk_get_too_many_ids (line 397) | def test_bulk_get_too_many_ids(self): method test_bulk_get_filters_other_users (line 404) | def test_bulk_get_filters_other_users(self): class BulkMetadataUpdateTestCase (line 416) | class BulkMetadataUpdateTestCase(APITestCase): method setUp (line 419) | def setUp(self): method test_bulk_update_rating (line 425) | def test_bulk_update_rating(self): method test_bulk_update_creates_history (line 436) | def test_bulk_update_creates_history(self): method test_bulk_update_no_ids (line 452) | def test_bulk_update_no_ids(self): method test_bulk_update_no_updates (line 461) | def test_bulk_update_no_updates(self): method test_bulk_update_invalid_field (line 470) | def test_bulk_update_invalid_field(self): method test_bulk_update_too_many_photos (line 482) | def test_bulk_update_too_many_photos(self): class PhotoMetadataEdgeCasesTestCase (line 493) | class PhotoMetadataEdgeCasesTestCase(APITestCase): method setUp (line 496) | def setUp(self): method test_photo_no_exif_data (line 502) | def test_photo_no_exif_data(self): method test_metadata_with_special_characters (line 517) | def test_metadata_with_special_characters(self): method test_metadata_empty_strings (line 529) | def test_metadata_empty_strings(self): method test_metadata_null_values (line 546) | def test_metadata_null_values(self): method test_concurrent_metadata_updates (line 556) | def test_concurrent_metadata_updates(self): method test_very_long_values (line 580) | def test_very_long_values(self): class PhotoMetadataModelTestCase (line 592) | class PhotoMetadataModelTestCase(TestCase): method setUp (line 595) | def setUp(self): method test_metadata_source_choices (line 599) | def test_metadata_source_choices(self): method test_has_location_property (line 610) | def test_has_location_property(self): method test_camera_display_property (line 626) | def test_camera_display_property(self): method test_lens_display_property (line 637) | def test_lens_display_property(self): class MetadataEditModelTestCase (line 649) | class MetadataEditModelTestCase(TestCase): method setUp (line 652) | def setUp(self): method test_create_edit_record (line 656) | def test_create_edit_record(self): method test_edit_record_timestamps (line 670) | def test_edit_record_timestamps(self): method test_edit_record_json_values (line 685) | def test_edit_record_json_values(self): method test_edit_record_null_old_value (line 698) | def test_edit_record_null_old_value(self): FILE: api/tests/test_photo_model_integration.py class PhotoModelIntegrationTest (line 11) | class PhotoModelIntegrationTest(TestCase): method setUp (line 12) | def setUp(self): method test_photo_properties_delegate_to_caption_model (line 16) | def test_photo_properties_delegate_to_caption_model(self): method test_photo_properties_delegate_to_search_model (line 35) | def test_photo_properties_delegate_to_search_model(self): method test_photo_caption_methods_work_directly (line 61) | def test_photo_caption_methods_work_directly(self): method test_photo_search_methods_work_directly (line 72) | def test_photo_search_methods_work_directly(self): method test_geolocate_updates_search_location (line 90) | def test_geolocate_updates_search_location(self): method test_cascade_deletion_of_related_models (line 119) | def test_cascade_deletion_of_related_models(self): method test_lazy_creation_of_related_instances (line 142) | def test_lazy_creation_of_related_instances(self): method test_get_or_create_methods (line 171) | def test_get_or_create_methods(self): method test_complex_workflow (line 193) | def test_complex_workflow(self): method test_property_error_handling (line 236) | def test_property_error_handling(self): method test_backward_compatibility (line 250) | def test_backward_compatibility(self): method test_queryset_only_with_search_location_fails (line 283) | def test_queryset_only_with_search_location_fails(self): method test_queryset_only_with_search_instance_works (line 289) | def test_queryset_only_with_search_instance_works(self): method test_album_date_queryset_works (line 309) | def test_album_date_queryset_works(self): FILE: api/tests/test_photo_search_model.py class PhotoSearchModelTest (line 13) | class PhotoSearchModelTest(TestCase): method setUp (line 14) | def setUp(self): method test_create_photo_search (line 18) | def test_create_photo_search(self): method test_photo_search_one_to_one_relationship (line 30) | def test_photo_search_one_to_one_relationship(self): method test_recreate_search_captions_with_places365 (line 40) | def test_recreate_search_captions_with_places365(self): method test_recreate_search_captions_with_user_caption (line 62) | def test_recreate_search_captions_with_user_caption(self): method test_recreate_search_captions_with_im2txt (line 74) | def test_recreate_search_captions_with_im2txt(self): method test_recreate_search_captions_with_faces (line 86) | def test_recreate_search_captions_with_faces(self): method test_recreate_search_captions_with_file_path (line 96) | def test_recreate_search_captions_with_file_path(self): method test_recreate_search_captions_with_video (line 104) | def test_recreate_search_captions_with_video(self): method test_recreate_search_captions_with_camera_info (line 112) | def test_recreate_search_captions_with_camera_info(self): method test_update_search_location (line 123) | def test_update_search_location(self): method test_update_search_location_with_empty_data (line 134) | def test_update_search_location_with_empty_data(self): method test_search_captions_default_empty (line 142) | def test_search_captions_default_empty(self): method test_search_location_default_empty (line 148) | def test_search_location_default_empty(self): method test_str_representation (line 154) | def test_str_representation(self): method test_cascade_delete_with_photo (line 163) | def test_cascade_delete_with_photo(self): method test_recreate_search_captions_comprehensive (line 173) | def test_recreate_search_captions_comprehensive(self): method test_search_captions_indexing (line 212) | def test_search_captions_indexing(self): method test_search_location_indexing (line 218) | def test_search_location_indexing(self): method test_empty_places365_handling (line 223) | def test_empty_places365_handling(self): method test_none_values_handling (line 238) | def test_none_values_handling(self): FILE: api/tests/test_photo_search_refactor.py class PhotoSearchRefactorTest (line 7) | class PhotoSearchRefactorTest(TestCase): method setUp (line 8) | def setUp(self): method test_search_location_property_works (line 13) | def test_search_location_property_works(self): method test_search_captions_property_works (line 28) | def test_search_captions_property_works(self): method test_recreate_search_captions_works (line 43) | def test_recreate_search_captions_works(self): method test_geolocate_updates_search_location (line 72) | def test_geolocate_updates_search_location(self): method test_direct_access_consistency (line 99) | def test_direct_access_consistency(self): FILE: api/tests/test_photo_summary.py class PhotoSummaryViewTest (line 9) | class PhotoSummaryViewTest(TestCase): method setUp (line 10) | def setUp(self): method test_summary_view_existing_photo_regular_user (line 17) | def test_summary_view_existing_photo_regular_user(self): method test_summary_view_existing_photo (line 28) | def test_summary_view_existing_photo(self): method test_summary_view_nonexistent_photo (line 36) | def test_summary_view_nonexistent_photo(self): method test_summary_view_no_aspect_ratio (line 42) | def test_summary_view_no_aspect_ratio(self): FILE: api/tests/test_photo_viewset_permissions.py class PhotoViewSetPermissionsTest (line 11) | class PhotoViewSetPermissionsTest(TestCase): method setUp (line 12) | def setUp(self): method test_owner_can_update_photo (line 18) | def test_owner_can_update_photo(self): method test_non_owner_cannot_update_photo (line 28) | def test_non_owner_cannot_update_photo(self): FILE: api/tests/test_predefined_rules.py class PredefinedRulesTest (line 10) | class PredefinedRulesTest(TestCase): method setUp (line 11) | def setUp(self): method test_predefined_rules (line 18) | def test_predefined_rules(self): method test_default_rules_on_predefined_rules_endpoint (line 27) | def test_default_rules_on_predefined_rules_endpoint(self): method test_default_rules_endpoint (line 33) | def test_default_rules_endpoint(self): method test_other_rules (line 38) | def test_other_rules(self): FILE: api/tests/test_public_photos.py class PublicPhotosTest (line 13) | class PublicPhotosTest(TestCase): method setUp (line 14) | def setUp(self): method test_set_my_photos_as_public (line 20) | def test_set_my_photos_as_public(self): method test_set_my_photos_as_private (line 36) | def test_set_my_photos_as_private(self): method test_set_photos_of_other_user_as_public (line 52) | def test_set_photos_of_other_user_as_public(self): method test_tag_nonexistent_photo_as_favorite (line 70) | def test_tag_nonexistent_photo_as_favorite(self, logger_ext: unittest.... FILE: api/tests/test_reading_exif.py class ReadFacesFromPhotosTest (line 12) | class ReadFacesFromPhotosTest(TestCase): method setUp (line 13) | def setUp(self): method test_reading_from_photo (line 18) | def test_reading_from_photo(self): FILE: api/tests/test_recently_added_photos.py class RecentlyAddedPhotosTest (line 9) | class RecentlyAddedPhotosTest(TestCase): method setUp (line 10) | def setUp(self): method test_retrieve_recently_added_photos (line 16) | def test_retrieve_recently_added_photos(self): method test_retrieve_empty_result_when_no_photos (line 29) | def test_retrieve_empty_result_when_no_photos(self): FILE: api/tests/test_redetection_idempotency.py class DuplicateRedetectionTestCase (line 31) | class DuplicateRedetectionTestCase(TestCase): method setUp (line 34) | def setUp(self): method test_exact_copy_redetection_no_duplicates (line 37) | def test_exact_copy_redetection_no_duplicates(self): method test_visual_duplicate_redetection_no_duplicates (line 61) | def test_visual_duplicate_redetection_no_duplicates(self): method test_redetection_adds_new_photos_to_existing_group (line 91) | def test_redetection_adds_new_photos_to_existing_group(self): method test_redetection_with_resolved_duplicates (line 122) | def test_redetection_with_resolved_duplicates(self): class BurstRedetectionTestCase (line 158) | class BurstRedetectionTestCase(TestCase): method setUp (line 161) | def setUp(self): method test_burst_redetection_no_duplicate_stacks (line 164) | def test_burst_redetection_no_duplicate_stacks(self): method test_redetection_adds_new_photos_to_existing_stack (line 194) | def test_redetection_adds_new_photos_to_existing_stack(self): class APIRedetectionTestCase (line 236) | class APIRedetectionTestCase(APITestCase): method setUp (line 239) | def setUp(self): method test_duplicate_detect_api_idempotent (line 248) | def test_duplicate_detect_api_idempotent(self): method test_stack_detect_api_idempotent (line 262) | def test_stack_detect_api_idempotent(self): method test_duplicate_detect_with_clear_pending (line 276) | def test_duplicate_detect_with_clear_pending(self): class PhotoInMultipleGroupsRedetectionTestCase (line 302) | class PhotoInMultipleGroupsRedetectionTestCase(TestCase): method setUp (line 305) | def setUp(self): method test_photo_already_in_stack_not_duplicated (line 308) | def test_photo_already_in_stack_not_duplicated(self): method test_duplicate_group_photos_not_duplicated (line 332) | def test_duplicate_group_photos_not_duplicated(self): class ClearExistingGroupsTestCase (line 361) | class ClearExistingGroupsTestCase(TestCase): method setUp (line 364) | def setUp(self): method test_clear_pending_duplicates (line 367) | def test_clear_pending_duplicates(self): method test_clear_pending_preserves_resolved (line 402) | def test_clear_pending_preserves_resolved(self): class MergeOnRedetectionTestCase (line 439) | class MergeOnRedetectionTestCase(TestCase): method setUp (line 442) | def setUp(self): method test_overlapping_stacks_merged (line 445) | def test_overlapping_stacks_merged(self): method test_overlapping_duplicates_merged (line 472) | def test_overlapping_duplicates_merged(self): class FileVariantsIdempotencyTestCase (line 500) | class FileVariantsIdempotencyTestCase(TestCase): method setUp (line 508) | def setUp(self): method test_photo_with_multiple_file_variants (line 511) | def test_photo_with_multiple_file_variants(self): method test_file_variant_types (line 549) | def test_file_variant_types(self): method test_has_raw_variant (line 588) | def test_has_raw_variant(self): method test_has_video_variant_live_photo (line 620) | def test_has_video_variant_live_photo(self): FILE: api/tests/test_regenerate_titles.py class RegenerateTitlesTestCase (line 9) | class RegenerateTitlesTestCase(TestCase): method test_regenerate_titles (line 10) | def test_regenerate_titles(self): FILE: api/tests/test_retrieve_photo.py class RetrievePhotoTest (line 7) | class RetrievePhotoTest(TestCase): method setUp (line 8) | def setUp(self): method test_should_retrieve_my_photo (line 13) | def test_should_retrieve_my_photo(self): method test_should_not_retrieve_other_user_photo (line 26) | def test_should_not_retrieve_other_user_photo(self): method test_anonymous_user_should_retrieve_public_photo (line 40) | def test_anonymous_user_should_retrieve_public_photo(self): method test_anonymous_user_should_not_retrieve_private_photo (line 53) | def test_anonymous_user_should_not_retrieve_private_photo(self): FILE: api/tests/test_scan_percentage_bug.py class ScanPercentageProgressTestCase (line 12) | class ScanPercentageProgressTestCase(TestCase): method setUp (line 13) | def setUp(self): method _scan_file_list (line 22) | def _scan_file_list(self): method _simulate_pre_fix_progress (line 30) | def _simulate_pre_fix_progress(self, files): method test_scan_progress_counts_every_discovered_file (line 51) | def test_scan_progress_counts_every_discovered_file(self): class EmptyDirectoryScanTestCase (line 142) | class EmptyDirectoryScanTestCase(TestCase): method setUp (line 145) | def setUp(self): method tearDown (line 156) | def tearDown(self): method test_empty_directory_scan_completes_successfully (line 162) | def test_empty_directory_scan_completes_successfully(self): FILE: api/tests/test_scan_photos.py class ScanPhotosTestCase (line 15) | class ScanPhotosTestCase(TestCase): method setUp (line 16) | def setUp(self): method test_setup (line 54) | def test_setup(self): method test_auto_albums (line 59) | def test_auto_albums(self): method test_place_albums (line 87) | def test_place_albums(self): method test_thing_albums (line 99) | def test_thing_albums(self): FILE: api/tests/test_scan_photos_directories.py class DummyAsyncTask (line 12) | class DummyAsyncTask: method __init__ (line 13) | def __init__(self, *args, **kwargs): method run (line 16) | def run(self): class DummyChain (line 20) | class DummyChain: method __init__ (line 21) | def __init__(self, *args, **kwargs): method append (line 24) | def append(self, *args, **kwargs): method run (line 28) | def run(self): class ScanPhotosDirectoryCreationTest (line 32) | class ScanPhotosDirectoryCreationTest(TestCase): method test_existing_thumbnail_directory_does_not_raise (line 33) | def test_existing_thumbnail_directory_does_not_raise(self): FILE: api/tests/test_search_term_examples.py class SearchTermExamplesTest (line 8) | class SearchTermExamplesTest(TestCase): method setUp (line 9) | def setUp(self): method test_get_search_term_examples_with_captions (line 14) | def test_get_search_term_examples_with_captions(self): method test_get_search_term_examples_with_empty_captions (line 49) | def test_get_search_term_examples_with_empty_captions(self): method test_search_term_examples_api_endpoint (line 84) | def test_search_term_examples_api_endpoint(self): FILE: api/tests/test_search_terms.py class GetSearchTermExamples (line 16) | class GetSearchTermExamples(TestCase): method setUp (line 17) | def setUp(self) -> None: method tearDown (line 48) | def tearDown(self) -> None: method test_get_search_term_examples_0 (line 54) | def test_get_search_term_examples_0(self): method test_get_search_term_examples_2 (line 60) | def test_get_search_term_examples_2(self): method test_get_search_term_examples_3 (line 74) | def test_get_search_term_examples_3(self): method test_get_search_term_examples_without_photos (line 91) | def test_get_search_term_examples_without_photos(self): FILE: api/tests/test_services.py class TestServiceCPUCompatibility (line 8) | class TestServiceCPUCompatibility(unittest.TestCase): method test_is_arm_architecture_detection (line 16) | def test_is_arm_architecture_detection(self, mock_machine): method test_check_cpu_features_on_arm (line 34) | def test_check_cpu_features_on_arm(self, mock_machine): method test_check_cpu_features_on_x86 (line 43) | def test_check_cpu_features_on_x86(self, mock_machine): method test_has_required_cpu_features_on_arm (line 54) | def test_has_required_cpu_features_on_arm(self, mock_machine): method test_has_required_cpu_features_no_requirements (line 63) | def test_has_required_cpu_features_no_requirements(self, mock_machine): method test_has_required_cpu_features_on_x86_with_features (line 73) | def test_has_required_cpu_features_on_x86_with_features(self, mock_fea... method test_has_required_cpu_features_on_x86_missing_required (line 83) | def test_has_required_cpu_features_on_x86_missing_required(self, mock_... method test_has_required_cpu_features_on_x86_missing_recommended (line 93) | def test_has_required_cpu_features_on_x86_missing_recommended(self, mo... FILE: api/tests/test_setup_directory.py class SetupDirectoryTestCase (line 8) | class SetupDirectoryTestCase(TestCase): method setUp (line 11) | def setUp(self): method test_setup_directory (line 17) | def test_setup_directory(self): method test_setup_not_existing_directory (line 25) | def test_setup_not_existing_directory(self): FILE: api/tests/test_share_photos.py class SharePhotosTest (line 13) | class SharePhotosTest(TestCase): method setUp (line 14) | def setUp(self): method test_share_photos (line 20) | def test_share_photos(self): method test_unshare_photos (line 46) | def test_unshare_photos(self): method test_share_other_user_photos (line 74) | def test_share_other_user_photos(self): FILE: api/tests/test_skip_raw_files.py class StackRawJpegTestCase (line 13) | class StackRawJpegTestCase(TestCase): method setUp (line 16) | def setUp(self): method tearDown (line 26) | def tearDown(self): method test_raw_files_always_valid (line 31) | def test_raw_files_always_valid(self): FILE: api/tests/test_social_graph.py class SocialGraphTestCase (line 12) | class SocialGraphTestCase(TestCase): method setUp (line 13) | def setUp(self): method test_build_social_graph_no_data (line 17) | def test_build_social_graph_no_data(self): method test_build_social_graph_single_person (line 22) | def test_build_social_graph_single_person(self): method test_build_social_graph_two_people_same_photo (line 49) | def test_build_social_graph_two_people_same_photo(self): FILE: api/tests/test_stack_api_edge_cases.py class ManualStackCreationAPITestCase (line 21) | class ManualStackCreationAPITestCase(TestCase): method setUp (line 24) | def setUp(self): method test_create_manual_stack_success (line 29) | def test_create_manual_stack_success(self): method test_create_manual_stack_minimum_photos (line 48) | def test_create_manual_stack_minimum_photos(self): method test_create_manual_stack_empty_photos (line 61) | def test_create_manual_stack_empty_photos(self): method test_create_manual_stack_nonexistent_photos (line 71) | def test_create_manual_stack_nonexistent_photos(self): method test_create_manual_stack_other_users_photos (line 82) | def test_create_manual_stack_other_users_photos(self): method test_create_manual_stack_duplicate_hashes (line 97) | def test_create_manual_stack_duplicate_hashes(self): class AddRemovePhotosAPITestCase (line 118) | class AddRemovePhotosAPITestCase(TestCase): method setUp (line 121) | def setUp(self): method test_add_photo_to_stack (line 126) | def test_add_photo_to_stack(self): method test_add_already_in_stack_photo (line 148) | def test_add_already_in_stack_photo(self): method test_remove_photo_from_stack (line 170) | def test_remove_photo_from_stack(self): method test_remove_to_one_photo_deletes_stack (line 192) | def test_remove_to_one_photo_deletes_stack(self): method test_remove_photo_not_in_stack (line 214) | def test_remove_photo_not_in_stack(self): class SetPrimaryPhotoAPITestCase (line 236) | class SetPrimaryPhotoAPITestCase(TestCase): method setUp (line 239) | def setUp(self): method test_set_primary_photo (line 244) | def test_set_primary_photo(self): method test_set_primary_photo_not_in_stack (line 266) | def test_set_primary_photo_not_in_stack(self): method test_set_primary_nonexistent_photo (line 287) | def test_set_primary_nonexistent_photo(self): class MergeStacksAPITestCase (line 307) | class MergeStacksAPITestCase(TestCase): method setUp (line 310) | def setUp(self): method test_merge_two_stacks (line 315) | def test_merge_two_stacks(self): method test_merge_nonexistent_stacks (line 348) | def test_merge_nonexistent_stacks(self): class StackStatsAPITestCase (line 363) | class StackStatsAPITestCase(TestCase): method setUp (line 366) | def setUp(self): method test_stats_with_no_stacks (line 371) | def test_stats_with_no_stacks(self): method test_stats_counts_by_type (line 378) | def test_stats_counts_by_type(self): method test_stats_photos_in_stacks (line 409) | def test_stats_photos_in_stacks(self): method test_stats_other_users_not_included (line 432) | def test_stats_other_users_not_included(self): class StackDeleteAPITestCase (line 451) | class StackDeleteAPITestCase(TestCase): method setUp (line 454) | def setUp(self): method test_delete_stack (line 459) | def test_delete_stack(self): method test_delete_nonexistent_stack (line 481) | def test_delete_nonexistent_stack(self): method test_delete_other_users_stack (line 486) | def test_delete_other_users_stack(self): class StackDetailAPITestCase (line 504) | class StackDetailAPITestCase(TestCase): method setUp (line 507) | def setUp(self): method test_get_stack_detail (line 512) | def test_get_stack_detail(self): method test_get_stack_with_deleted_primary (line 530) | def test_get_stack_with_deleted_primary(self): class StackListAPITestCase (line 556) | class StackListAPITestCase(TestCase): method setUp (line 559) | def setUp(self): method test_list_stacks (line 564) | def test_list_stacks(self): method test_list_stacks_filter_by_type (line 585) | def test_list_stacks_filter_by_type(self): method test_list_excludes_single_photo_stacks (line 606) | def test_list_excludes_single_photo_stacks(self): class DetectionTriggerAPITestCase (line 622) | class DetectionTriggerAPITestCase(TestCase): method setUp (line 625) | def setUp(self): method test_trigger_detection (line 630) | def test_trigger_detection(self): method test_trigger_detection_empty_body (line 645) | def test_trigger_detection_empty_body(self): FILE: api/tests/test_stack_detection.py class PhotoStackModelTestCase (line 24) | class PhotoStackModelTestCase(TestCase): method setUp (line 27) | def setUp(self): method test_create_stack_basic (line 33) | def test_create_stack_basic(self): method test_stack_types (line 46) | def test_stack_types(self): method test_auto_select_primary_for_manual_stack (line 56) | def test_auto_select_primary_for_manual_stack(self): method test_auto_select_primary_empty_stack (line 71) | def test_auto_select_primary_empty_stack(self): method test_merge_with_another_stack (line 83) | def test_merge_with_another_stack(self): method test_merge_with_self_does_nothing (line 110) | def test_merge_with_self_does_nothing(self): method test_create_or_merge_new_stack (line 124) | def test_create_or_merge_new_stack(self): method test_create_or_merge_returns_none_for_single_photo (line 138) | def test_create_or_merge_returns_none_for_single_photo(self): method test_create_or_merge_merges_existing_stacks (line 148) | def test_create_or_merge_merges_existing_stacks(self): method test_photo_can_be_in_multiple_stacks_of_different_types (line 169) | def test_photo_can_be_in_multiple_stacks_of_different_types(self): class PhotoStackAPITestCase (line 186) | class PhotoStackAPITestCase(TestCase): method setUp (line 189) | def setUp(self): method test_list_stacks_returns_user_stacks (line 209) | def test_list_stacks_returns_user_stacks(self): method test_list_stacks_excludes_other_user_stacks (line 221) | def test_list_stacks_excludes_other_user_stacks(self): method test_list_stacks_excludes_stacks_with_less_than_2_photos (line 239) | def test_list_stacks_excludes_stacks_with_less_than_2_photos(self): method test_list_stacks_filter_by_type (line 253) | def test_list_stacks_filter_by_type(self): method test_list_stacks_pagination (line 270) | def test_list_stacks_pagination(self): method test_get_stack_detail (line 282) | def test_get_stack_detail(self): method test_get_stack_detail_not_found (line 294) | def test_get_stack_detail_not_found(self): method test_get_stack_detail_other_user_returns_404 (line 301) | def test_get_stack_detail_other_user_returns_404(self): method test_delete_stack (line 316) | def test_delete_stack(self): method test_delete_stack_not_found (line 336) | def test_delete_stack_not_found(self): method test_set_primary_photo (line 343) | def test_set_primary_photo(self): method test_set_primary_photo_not_in_stack (line 360) | def test_set_primary_photo_not_in_stack(self): method test_set_primary_missing_photo_hash (line 371) | def test_set_primary_missing_photo_hash(self): method test_add_photos_to_stack (line 381) | def test_add_photos_to_stack(self): method test_add_photos_already_in_stack (line 398) | def test_add_photos_already_in_stack(self): method test_remove_photos_from_stack (line 411) | def test_remove_photos_from_stack(self): method test_remove_photos_deletes_stack_if_less_than_2_remain (line 428) | def test_remove_photos_deletes_stack_if_less_than_2_remain(self): method test_unauthenticated_request_returns_401 (line 445) | def test_unauthenticated_request_returns_401(self): class ManualStackCreationTestCase (line 454) | class ManualStackCreationTestCase(TestCase): method setUp (line 457) | def setUp(self): method test_create_manual_stack (line 466) | def test_create_manual_stack(self): method test_create_manual_stack_requires_at_least_2_photos (line 485) | def test_create_manual_stack_requires_at_least_2_photos(self): method test_create_manual_stack_invalid_photo_hash (line 495) | def test_create_manual_stack_invalid_photo_hash(self): method test_create_manual_stack_other_user_photo (line 506) | def test_create_manual_stack_other_user_photo(self): method test_create_manual_stack_adds_to_existing_if_already_stacked (line 519) | def test_create_manual_stack_adds_to_existing_if_already_stacked(self): class MergeStacksTestCase (line 544) | class MergeStacksTestCase(TestCase): method setUp (line 547) | def setUp(self): method test_merge_stacks (line 557) | def test_merge_stacks(self): method test_merge_single_stack_no_merge_needed (line 607) | def test_merge_single_stack_no_merge_needed(self): method test_merge_no_stacks_returns_error (line 626) | def test_merge_no_stacks_returns_error(self): method test_merge_missing_photo_hashes (line 636) | def test_merge_missing_photo_hashes(self): class StackDetectionTestCase (line 647) | class StackDetectionTestCase(TestCase): method setUp (line 650) | def setUp(self): method test_detect_stacks_queues_background_job (line 656) | def test_detect_stacks_queues_background_job(self, mock_async_task): method test_detect_stacks_with_options (line 669) | def test_detect_stacks_with_options(self, mock_async_task): class StackStatsTestCase (line 689) | class StackStatsTestCase(TestCase): method setUp (line 692) | def setUp(self): method test_get_stack_stats (line 702) | def test_get_stack_stats(self): class StackEdgeCasesTestCase (line 730) | class StackEdgeCasesTestCase(TestCase): method setUp (line 733) | def setUp(self): method test_invalid_uuid_format_in_url (line 741) | def test_invalid_uuid_format_in_url(self): method test_stack_with_deleted_photo_handles_gracefully (line 754) | def test_stack_with_deleted_photo_handles_gracefully(self): method test_empty_photo_hashes_array (line 776) | def test_empty_photo_hashes_array(self): method test_duplicate_photo_hashes_in_request (line 786) | def test_duplicate_photo_hashes_in_request(self): method test_remove_primary_photo_auto_selects_new_primary (line 797) | def test_remove_primary_photo_auto_selects_new_primary(self): method test_stack_list_page_size_max_100 (line 824) | def test_stack_list_page_size_max_100(self): FILE: api/tests/test_stack_detection_edge_cases.py class CreateOrMergeQuerysetOrderingTestCase (line 35) | class CreateOrMergeQuerysetOrderingTestCase(TestCase): method setUp (line 48) | def setUp(self): method test_create_or_merge_with_multiple_existing_stacks (line 51) | def test_create_or_merge_with_multiple_existing_stacks(self): method test_create_or_merge_with_three_existing_stacks (line 104) | def test_create_or_merge_with_three_existing_stacks(self): class DuplicateCreateOrMergeOrderingTestCase (line 150) | class DuplicateCreateOrMergeOrderingTestCase(TestCase): method setUp (line 153) | def setUp(self): method test_duplicate_create_or_merge_with_multiple_groups (line 156) | def test_duplicate_create_or_merge_with_multiple_groups(self): class HardCriteriaBurstDetectionEdgeCasesTestCase (line 193) | class HardCriteriaBurstDetectionEdgeCasesTestCase(TestCase): method setUp (line 196) | def setUp(self): method _create_file (line 212) | def _create_file(self, path, file_type=File.IMAGE): method _create_photo_with_file (line 220) | def _create_photo_with_file(self, path, **kwargs): method test_all_photos_without_main_file (line 228) | def test_all_photos_without_main_file(self): method test_get_metadata_raises_exception_for_all (line 242) | def test_get_metadata_raises_exception_for_all(self, mock_get_metadata): method test_get_metadata_returns_empty_for_some (line 254) | def test_get_metadata_returns_empty_for_some(self, mock_get_metadata): class SoftCriteriaBurstDetectionEdgeCasesTestCase (line 267) | class SoftCriteriaBurstDetectionEdgeCasesTestCase(TestCase): method setUp (line 270) | def setUp(self): method _create_file (line 273) | def _create_file(self, path, file_type=File.IMAGE): method _create_photo_with_timestamp (line 281) | def _create_photo_with_timestamp(self, timestamp, perceptual_hash=None... method test_visual_similarity_with_null_perceptual_hash (line 292) | def test_visual_similarity_with_null_perceptual_hash(self): method test_timestamp_proximity_with_same_timestamp (line 319) | def test_timestamp_proximity_with_same_timestamp(self): method test_timestamp_proximity_boundary_condition (line 353) | def test_timestamp_proximity_boundary_condition(self): method test_all_photos_already_in_burst_stacks (line 392) | def test_all_photos_already_in_burst_stacks(self): class FileVariantEdgeCasesTestCase (line 426) | class FileVariantEdgeCasesTestCase(TestCase): method setUp (line 434) | def setUp(self): method _create_file (line 437) | def _create_file(self, path, file_type=File.IMAGE): method test_multiple_raw_files_same_photo (line 445) | def test_multiple_raw_files_same_photo(self): method test_video_and_raw_same_photo (line 464) | def test_video_and_raw_same_photo(self): method test_dot_in_basename (line 488) | def test_dot_in_basename(self): method test_very_long_filename (line 503) | def test_very_long_filename(self): method test_whitespace_in_path (line 518) | def test_whitespace_in_path(self): method test_deeply_nested_path (line 532) | def test_deeply_nested_path(self): class MalformedRulesEdgeCasesTestCase (line 548) | class MalformedRulesEdgeCasesTestCase(TestCase): method setUp (line 551) | def setUp(self): method test_invalid_json_string_rules (line 554) | def test_invalid_json_string_rules(self): method test_rules_as_dict_instead_of_list (line 563) | def test_rules_as_dict_instead_of_list(self): method test_rules_with_missing_required_fields (line 578) | def test_rules_with_missing_required_fields(self): method test_rules_with_unknown_rule_type (line 598) | def test_rules_with_unknown_rule_type(self): class BatchDetectionEdgeCasesTestCase (line 618) | class BatchDetectionEdgeCasesTestCase(TestCase): method setUp (line 621) | def setUp(self): method test_partial_options (line 626) | def test_partial_options(self): method test_empty_options_dict (line 639) | def test_empty_options_dict(self): method test_exception_in_detector (line 650) | def test_exception_in_detector(self, mock_burst): class ClearStacksEdgeCasesTestCase (line 662) | class ClearStacksEdgeCasesTestCase(TestCase): method setUp (line 665) | def setUp(self): method test_clear_with_photo_in_multiple_stacks (line 668) | def test_clear_with_photo_in_multiple_stacks(self): method test_clear_with_many_stacks (line 694) | def test_clear_with_many_stacks(self): class SequenceTimestampEdgeCasesTestCase (line 718) | class SequenceTimestampEdgeCasesTestCase(TestCase): method setUp (line 721) | def setUp(self): method test_burst_stack_with_none_timestamps (line 724) | def test_burst_stack_with_none_timestamps(self): method test_create_or_merge_updates_sequence_timestamps (line 741) | def test_create_or_merge_updates_sequence_timestamps(self): class FilenamePatternEdgeCasesTestCase (line 782) | class FilenamePatternEdgeCasesTestCase(TestCase): method setUp (line 785) | def setUp(self): method _create_file (line 802) | def _create_file(self, path, file_type=File.IMAGE): method _create_photo_with_file (line 810) | def _create_photo_with_file(self, path, **kwargs): method test_regex_pattern_special_chars (line 818) | def test_regex_pattern_special_chars(self): method test_empty_filename (line 845) | def test_empty_filename(self): FILE: api/tests/test_stack_detection_logic.py class ClearStacksTestCase (line 35) | class ClearStacksTestCase(TestCase): method setUp (line 38) | def setUp(self): method test_clear_stacks_removes_all_of_type (line 42) | def test_clear_stacks_removes_all_of_type(self): method test_clear_stacks_unlinks_photos (line 74) | def test_clear_stacks_unlinks_photos(self): method test_clear_stacks_only_affects_user (line 92) | def test_clear_stacks_only_affects_user(self): method test_clear_stacks_returns_zero_if_none (line 118) | def test_clear_stacks_returns_zero_if_none(self): class BurstDetectionTestCase (line 124) | class BurstDetectionTestCase(TestCase): method setUp (line 127) | def setUp(self): method _create_file (line 149) | def _create_file(self, path, file_type=File.IMAGE): method _create_photo_with_timestamp (line 157) | def _create_photo_with_timestamp(self, timestamp, **kwargs): method test_no_rules_returns_zero (line 166) | def test_no_rules_returns_zero(self): method test_disabled_rules_ignored (line 178) | def test_disabled_rules_ignored(self): method test_clears_existing_burst_stacks (line 199) | def test_clears_existing_burst_stacks(self): method test_skip_hidden_photos (line 214) | def test_skip_hidden_photos(self): method test_skip_trashed_photos (line 224) | def test_skip_trashed_photos(self): class CreateBurstStackTestCase (line 235) | class CreateBurstStackTestCase(TestCase): method setUp (line 238) | def setUp(self): method test_requires_minimum_two_photos (line 241) | def test_requires_minimum_two_photos(self): method test_creates_stack_with_two_photos (line 249) | def test_creates_stack_with_two_photos(self): method test_skips_already_stacked_photos (line 264) | def test_skips_already_stacked_photos(self): class BatchDetectStacksTestCase (line 284) | class BatchDetectStacksTestCase(TestCase): method setUp (line 287) | def setUp(self): method test_calls_burst_detector_by_default (line 293) | def test_calls_burst_detector_by_default(self, mock_burst): method test_respects_options (line 302) | def test_respects_options(self, mock_burst): method test_creates_job (line 313) | def test_creates_job(self, mock_burst): method test_handles_exception (line 326) | def test_handles_exception(self, mock_burst): class FileExtensionsTestCase (line 340) | class FileExtensionsTestCase(TestCase): method test_jpeg_extensions_included (line 343) | def test_jpeg_extensions_included(self): method test_extensions_are_lowercase (line 349) | def test_extensions_are_lowercase(self): class EdgeCasesTestCase (line 355) | class EdgeCasesTestCase(TestCase): method setUp (line 358) | def setUp(self): method test_photo_without_main_file (line 361) | def test_photo_without_main_file(self): method test_empty_photo_library (line 384) | def test_empty_photo_library(self): method test_burst_rules_as_string (line 401) | def test_burst_rules_as_string(self): class FileVariantTestCase (line 410) | class FileVariantTestCase(TestCase): method setUp (line 418) | def setUp(self): method _create_file (line 421) | def _create_file(self, path, file_type=File.IMAGE): method test_photo_with_raw_variant (line 429) | def test_photo_with_raw_variant(self): method test_photo_with_live_photo_video (line 448) | def test_photo_with_live_photo_video(self): method test_photo_with_all_variant_types (line 467) | def test_photo_with_all_variant_types(self): method test_file_variant_vs_stack (line 485) | def test_file_variant_vs_stack(self): method test_special_characters_in_filename (line 511) | def test_special_characters_in_filename(self): method test_unicode_in_filename (line 524) | def test_unicode_in_filename(self): method test_progress_callback_called (line 537) | def test_progress_callback_called(self): FILE: api/tests/test_stack_duplicate_integration.py class PhotoInBothStackAndDuplicateTestCase (line 23) | class PhotoInBothStackAndDuplicateTestCase(TestCase): method setUp (line 26) | def setUp(self): method _create_photos (line 31) | def _create_photos(self, count): method test_photo_can_be_in_stack_and_duplicate_simultaneously (line 50) | def test_photo_can_be_in_stack_and_duplicate_simultaneously(self): method test_resolving_duplicate_trashes_stacked_photo (line 76) | def test_resolving_duplicate_trashes_stacked_photo(self): method test_resolving_duplicate_trashes_stack_primary (line 108) | def test_resolving_duplicate_trashes_stack_primary(self): method test_deleting_stack_does_not_affect_duplicate (line 136) | def test_deleting_stack_does_not_affect_duplicate(self): method test_dismissing_duplicate_does_not_affect_stack (line 160) | def test_dismissing_duplicate_does_not_affect_stack(self): class PhotoDeletionCascadeTestCase (line 186) | class PhotoDeletionCascadeTestCase(TestCase): method setUp (line 189) | def setUp(self): method _create_photo (line 193) | def _create_photo(self, suffix): method test_deleting_photo_removes_from_stack (line 207) | def test_deleting_photo_removes_from_stack(self): method test_deleting_photo_removes_from_duplicate (line 228) | def test_deleting_photo_removes_from_duplicate(self): method test_deleting_stack_primary_sets_to_null (line 248) | def test_deleting_stack_primary_sets_to_null(self): method test_deleting_kept_photo_sets_to_null (line 268) | def test_deleting_kept_photo_sets_to_null(self): class MultiUserIsolationTestCase (line 288) | class MultiUserIsolationTestCase(TestCase): method setUp (line 291) | def setUp(self): method _create_photo_for_user (line 296) | def _create_photo_for_user(self, user, suffix): method test_users_cannot_share_stacks (line 310) | def test_users_cannot_share_stacks(self): method test_duplicate_detection_scoped_to_user (line 330) | def test_duplicate_detection_scoped_to_user(self): class StackMergeWithDuplicatesTestCase (line 353) | class StackMergeWithDuplicatesTestCase(TestCase): method setUp (line 356) | def setUp(self): method test_merging_stacks_preserves_duplicate_membership (line 374) | def test_merging_stacks_preserves_duplicate_membership(self): class PhotoInMultipleStacksTestCase (line 412) | class PhotoInMultipleStacksTestCase(TestCase): method setUp (line 415) | def setUp(self): method _create_photo (line 419) | def _create_photo(self, suffix): method test_photo_can_be_in_multiple_stacks (line 433) | def test_photo_can_be_in_multiple_stacks(self): method test_photo_can_be_primary_in_multiple_stacks (line 451) | def test_photo_can_be_primary_in_multiple_stacks(self): method test_removing_photo_from_one_stack_preserves_others (line 472) | def test_removing_photo_from_one_stack_preserves_others(self): class DuplicateMergeWithStacksTestCase (line 496) | class DuplicateMergeWithStacksTestCase(TestCase): method setUp (line 499) | def setUp(self): method test_merging_duplicates_preserves_stack_membership (line 517) | def test_merging_duplicates_preserves_stack_membership(self): class EdgeCasesTestCase (line 552) | class EdgeCasesTestCase(TestCase): method setUp (line 555) | def setUp(self): method _create_photo (line 559) | def _create_photo(self, suffix): method test_empty_stack_and_duplicate (line 573) | def test_empty_stack_and_duplicate(self): method test_single_photo_in_both (line 587) | def test_single_photo_in_both(self): method test_trashing_all_photos_in_stack (line 606) | def test_trashing_all_photos_in_stack(self): method test_photo_in_multiple_duplicate_groups (line 628) | def test_photo_in_multiple_duplicate_groups(self): method test_resolving_duplicate_does_not_affect_other_duplicates (line 646) | def test_resolving_duplicate_does_not_affect_other_duplicates(self): method test_cascade_effects_are_contained (line 675) | def test_cascade_effects_are_contained(self): FILE: api/tests/test_stack_review.py class StackReviewModelTestCase (line 31) | class StackReviewModelTestCase(TestCase): method setUp (line 34) | def setUp(self): method test_create_stack_review (line 42) | def test_create_stack_review(self): method test_uuid_primary_key (line 54) | def test_uuid_primary_key(self): method test_one_to_one_with_stack (line 62) | def test_one_to_one_with_stack(self): method test_str_representation (line 76) | def test_str_representation(self): method test_decision_choices (line 87) | def test_decision_choices(self): method test_optional_note_field (line 100) | def test_optional_note_field(self): method test_ordering_by_created_at_descending (line 109) | def test_ordering_by_created_at_descending(self): class DecisionChoicesTestCase (line 129) | class DecisionChoicesTestCase(TestCase): method test_pending_choice (line 132) | def test_pending_choice(self): method test_resolved_choice (line 136) | def test_resolved_choice(self): method test_dismissed_choice (line 140) | def test_dismissed_choice(self): method test_all_choices_have_labels (line 144) | def test_all_choices_have_labels(self): class IsReviewableTypeTestCase (line 152) | class IsReviewableTypeTestCase(TestCase): method test_no_stack_types_are_reviewable (line 155) | def test_no_stack_types_are_reviewable(self): method test_manual_not_reviewable (line 169) | def test_manual_not_reviewable(self): method test_burst_not_reviewable (line 173) | def test_burst_not_reviewable(self): class CreateForStackTestCase (line 178) | class CreateForStackTestCase(TestCase): method setUp (line 181) | def setUp(self): method test_returns_none_for_all_stack_types (line 185) | def test_returns_none_for_all_stack_types(self): method test_returns_none_for_manual_stack (line 198) | def test_returns_none_for_manual_stack(self): method test_returns_none_for_burst_stack (line 207) | def test_returns_none_for_burst_stack(self): class ResolveTestCase (line 217) | class ResolveTestCase(TestCase): method setUp (line 220) | def setUp(self): method test_resolve_sets_kept_photo (line 255) | def test_resolve_sets_kept_photo(self): method test_resolve_sets_decision_to_resolved (line 263) | def test_resolve_sets_decision_to_resolved(self): method test_resolve_sets_reviewed_at (line 270) | def test_resolve_sets_reviewed_at(self): method test_resolve_sets_stack_primary_photo (line 279) | def test_resolve_sets_stack_primary_photo(self): method test_resolve_trashes_other_photos (line 287) | def test_resolve_trashes_other_photos(self): method test_resolve_sets_trashed_count (line 299) | def test_resolve_sets_trashed_count(self): method test_resolve_without_trashing (line 306) | def test_resolve_without_trashing(self): method test_resolve_returns_self (line 314) | def test_resolve_returns_self(self): class DismissTestCase (line 320) | class DismissTestCase(TestCase): method setUp (line 323) | def setUp(self): method test_dismiss_sets_decision_to_dismissed (line 357) | def test_dismiss_sets_decision_to_dismissed(self): method test_dismiss_sets_reviewed_at (line 364) | def test_dismiss_sets_reviewed_at(self): method test_dismiss_unlinks_photos_from_stack (line 371) | def test_dismiss_unlinks_photos_from_stack(self): method test_dismiss_returns_self (line 382) | def test_dismiss_returns_self(self): method test_dismiss_photos_still_exist (line 387) | def test_dismiss_photos_still_exist(self): class RevertTestCase (line 397) | class RevertTestCase(TestCase): method setUp (line 400) | def setUp(self): method test_revert_restores_trashed_photos (line 436) | def test_revert_restores_trashed_photos(self): method test_revert_resets_decision_to_pending (line 452) | def test_revert_resets_decision_to_pending(self): method test_revert_clears_kept_photo (line 459) | def test_revert_clears_kept_photo(self): method test_revert_clears_trashed_count (line 466) | def test_revert_clears_trashed_count(self): method test_revert_clears_reviewed_at (line 473) | def test_revert_clears_reviewed_at(self): method test_revert_clears_stack_primary_photo (line 480) | def test_revert_clears_stack_primary_photo(self): method test_revert_returns_restored_count (line 487) | def test_revert_returns_restored_count(self): method test_revert_does_nothing_if_not_resolved (line 492) | def test_revert_does_nothing_if_not_resolved(self): method test_revert_does_nothing_if_dismissed (line 511) | def test_revert_does_nothing_if_dismissed(self): class EdgeCasesTestCase (line 523) | class EdgeCasesTestCase(TestCase): method setUp (line 526) | def setUp(self): method test_resolve_with_single_photo_stack (line 530) | def test_resolve_with_single_photo_stack(self): method test_dismiss_empty_stack (line 559) | def test_dismiss_empty_stack(self): method test_revert_when_photos_already_restored (line 576) | def test_revert_when_photos_already_restored(self): method test_stack_cascade_delete (line 608) | def test_stack_cascade_delete(self): method test_user_set_to_deleted_on_reviewer_delete (line 624) | def test_user_set_to_deleted_on_reviewer_delete(self): method test_review_with_note (line 641) | def test_review_with_note(self): method test_review_index_on_reviewer_decision (line 656) | def test_review_index_on_reviewer_decision(self): FILE: api/tests/test_stack_validation_edge_cases.py class DuplicatePhotoHashesTestCase (line 17) | class DuplicatePhotoHashesTestCase(TestCase): method setUp (line 20) | def setUp(self): method test_create_manual_stack_with_duplicate_valid_hashes (line 25) | def test_create_manual_stack_with_duplicate_valid_hashes(self): method test_create_manual_stack_triple_duplicate_same_hash (line 53) | def test_create_manual_stack_triple_duplicate_same_hash(self): method test_add_photos_with_duplicate_hashes (line 66) | def test_add_photos_with_duplicate_hashes(self): method test_remove_photos_with_duplicate_hashes (line 90) | def test_remove_photos_with_duplicate_hashes(self): class MergeStacksDuplicateHashesTestCase (line 113) | class MergeStacksDuplicateHashesTestCase(TestCase): method setUp (line 116) | def setUp(self): method test_merge_with_duplicate_hashes (line 121) | def test_merge_with_duplicate_hashes(self): class ListStacksWithNullThumbnailTestCase (line 160) | class ListStacksWithNullThumbnailTestCase(TestCase): method setUp (line 163) | def setUp(self): method test_list_stacks_without_thumbnails (line 168) | def test_list_stacks_without_thumbnails(self): method test_detail_stack_without_thumbnails (line 194) | def test_detail_stack_without_thumbnails(self): class RemoveFromStackPrimaryPhotoTestCase (line 213) | class RemoveFromStackPrimaryPhotoTestCase(TestCase): method setUp (line 216) | def setUp(self): method test_remove_primary_updates_primary (line 221) | def test_remove_primary_updates_primary(self): method test_remove_non_primary_keeps_primary (line 248) | def test_remove_non_primary_keeps_primary(self): class EmptyInputTestCase (line 275) | class EmptyInputTestCase(TestCase): method setUp (line 278) | def setUp(self): method test_set_primary_no_hash (line 283) | def test_set_primary_no_hash(self): method test_set_primary_empty_hash (line 303) | def test_set_primary_empty_hash(self): method test_add_empty_photo_list (line 322) | def test_add_empty_photo_list(self): method test_remove_empty_photo_list (line 341) | def test_remove_empty_photo_list(self): method test_merge_empty_photo_list (line 360) | def test_merge_empty_photo_list(self): method test_create_manual_missing_photo_hashes_key (line 370) | def test_create_manual_missing_photo_hashes_key(self): class StackListPaginationEdgeCasesTestCase (line 381) | class StackListPaginationEdgeCasesTestCase(TestCase): method setUp (line 384) | def setUp(self): method test_page_beyond_results (line 398) | def test_page_beyond_results(self): method test_page_zero (line 409) | def test_page_zero(self): method test_negative_page (line 417) | def test_negative_page(self): method test_non_numeric_page (line 425) | def test_non_numeric_page(self): method test_page_size_zero (line 433) | def test_page_size_zero(self): method test_page_size_negative (line 441) | def test_page_size_negative(self): method test_page_size_exceeds_max (line 448) | def test_page_size_exceeds_max(self): method test_non_numeric_page_size (line 456) | def test_non_numeric_page_size(self): FILE: api/tests/test_stats_accuracy.py class DuplicateStatsAccuracyTestCase (line 22) | class DuplicateStatsAccuracyTestCase(APITestCase): method setUp (line 25) | def setUp(self): method test_empty_stats (line 30) | def test_empty_stats(self): method test_stats_by_type (line 39) | def test_stats_by_type(self): method test_stats_by_status (line 64) | def test_stats_by_status(self): method test_photos_in_duplicates_count (line 101) | def test_photos_in_duplicates_count(self): method test_potential_savings_calculation (line 116) | def test_potential_savings_calculation(self): method test_stats_user_scoped (line 143) | def test_stats_user_scoped(self): class StackStatsAccuracyTestCase (line 169) | class StackStatsAccuracyTestCase(APITestCase): method setUp (line 172) | def setUp(self): method test_empty_stats (line 177) | def test_empty_stats(self): method test_stats_by_type (line 184) | def test_stats_by_type(self): method test_photos_in_stacks_count (line 218) | def test_photos_in_stacks_count(self): method test_photo_in_multiple_stacks_counted_once (line 242) | def test_photo_in_multiple_stacks_counted_once(self): method test_stats_user_scoped (line 265) | def test_stats_user_scoped(self): method test_excludes_duplicate_type_stacks (line 290) | def test_excludes_duplicate_type_stacks(self): class UtilityFunctionsTestCase (line 308) | class UtilityFunctionsTestCase(TestCase): method test_calc_megabytes_zero (line 311) | def test_calc_megabytes_zero(self): method test_calc_megabytes_none (line 315) | def test_calc_megabytes_none(self): method test_calc_megabytes_small (line 319) | def test_calc_megabytes_small(self): method test_calc_megabytes_large (line 324) | def test_calc_megabytes_large(self): method test_median_value_empty_queryset (line 329) | def test_median_value_empty_queryset(self): class CountStatsTestCase (line 336) | class CountStatsTestCase(TestCase): method setUp (line 339) | def setUp(self): method test_count_stats_no_photos (line 342) | def test_count_stats_no_photos(self): method test_count_stats_with_photos (line 347) | def test_count_stats_with_photos(self): method test_count_stats_excludes_hidden (line 356) | def test_count_stats_excludes_hidden(self): method test_count_stats_user_scoped (line 371) | def test_count_stats_user_scoped(self): class StatsEdgeCasesTestCase (line 387) | class StatsEdgeCasesTestCase(APITestCase): method setUp (line 390) | def setUp(self): method test_duplicate_with_zero_photos (line 395) | def test_duplicate_with_zero_photos(self): method test_stack_with_single_photo (line 408) | def test_stack_with_single_photo(self): method test_deleted_photo_in_group (line 422) | def test_deleted_photo_in_group(self): method test_trashed_photos_excluded (line 439) | def test_trashed_photos_excluded(self): method test_large_number_of_groups (line 455) | def test_large_number_of_groups(self): FILE: api/tests/test_thumbnail_migration.py class ThumbnailMigrationTest (line 15) | class ThumbnailMigrationTest(TestCase): method setUp (line 18) | def setUp(self): method tearDown (line 23) | def tearDown(self): method test_batch_processing_logic (line 30) | def test_batch_processing_logic(self): method test_file_renaming_with_mocked_filesystem (line 49) | def test_file_renaming_with_mocked_filesystem(self, mock_media_root): method test_bulk_update_performance (line 120) | def test_bulk_update_performance(self): FILE: api/tests/test_thumbnail_naming.py class ThumbnailNamingTest (line 13) | class ThumbnailNamingTest(TestCase): method setUp (line 16) | def setUp(self): method test_thumbnail_uses_image_hash_not_uuid (line 19) | def test_thumbnail_uses_image_hash_not_uuid(self): method test_generate_thumbnail_uses_image_hash (line 45) | def test_generate_thumbnail_uses_image_hash(self, mock_exists, mock_cr... method test_thumbnail_file_naming_convention (line 68) | def test_thumbnail_file_naming_convention(self): method test_video_thumbnail_naming (line 82) | def test_video_thumbnail_naming(self): FILE: api/tests/test_trash_api.py class TrashAPITest (line 7) | class TrashAPITest(TestCase): method setUp (line 8) | def setUp(self): method test_trash_api_returns_deleted_images (line 13) | def test_trash_api_returns_deleted_images(self): method test_trash_api_without_folder_parameter (line 59) | def test_trash_api_without_folder_parameter(self): FILE: api/tests/test_user.py function delete_all_users (line 15) | def delete_all_users(): class UserTest (line 21) | class UserTest(TestCase): method setUp (line 77) | def setUp(self): method test_public_user_list_count (line 84) | def test_public_user_list_count(self): method test_public_user_list_properties (line 91) | def test_public_user_list_properties(self): method test_authenticated_user_list_count (line 99) | def test_authenticated_user_list_count(self): method test_authenticated_user_list_properties (line 105) | def test_authenticated_user_list_properties(self): method test_user_update_self (line 122) | def test_user_update_self(self): method test_public_update_user (line 129) | def test_public_update_user(self): method test_public_delete_user (line 135) | def test_public_delete_user(self): method test_super_user_create_with_command (line 140) | def test_super_user_create_with_command(self): method test_public_user_create_successful_on_first_setup (line 149) | def test_public_user_create_successful_on_first_setup(self): method test_public_user_create_successful_when_registration_enabled (line 160) | def test_public_user_create_successful_when_registration_enabled(self): method test_after_registration_user_can_authenticate (line 171) | def test_after_registration_user_can_authenticate(self): method test_public_user_create_fails_when_registration_disabled (line 186) | def test_public_user_create_fails_when_registration_disabled(self): method test_not_first_setup_create_admin_should_create_regular_user (line 193) | def test_not_first_setup_create_admin_should_create_regular_user(self): method test_authenticated_user_cannot_create_superuser (line 201) | def test_authenticated_user_cannot_create_superuser(self): method test_user_update_another_user (line 211) | def test_user_update_another_user(self): method test_user_delete_another_user (line 218) | def test_user_delete_another_user(self): method test_admin_create_user (line 223) | def test_admin_create_user(self): method test_admin_partial_update_user (line 228) | def test_admin_partial_update_user(self): method test_admin_delete_user (line 235) | def test_admin_delete_user(self): method test_first_time_setup_creates_user_when_registration_is_disabled (line 241) | def test_first_time_setup_creates_user_when_registration_is_disabled(s... method test_first_time_setup (line 246) | def test_first_time_setup(self): method test_not_first_time_setup (line 253) | def test_not_first_time_setup(self): method test_regular_user_not_allowed_to_set_scan_directory (line 263) | def test_regular_user_not_allowed_to_set_scan_directory(self): FILE: api/tests/test_xmp_association.py function create_unique_png (line 19) | def create_unique_png(seed=0): class XMPAssociationTest (line 33) | class XMPAssociationTest(TestCase): method test_xmp_association_after_image_creation (line 41) | def test_xmp_association_after_image_creation(self): method test_xmp_processed_before_image_fails_gracefully (line 115) | def test_xmp_processed_before_image_fails_gracefully(self): method test_metadata_function_finds_matching_photo (line 150) | def test_metadata_function_finds_matching_photo(self): FILE: api/tests/test_zip_list_photos_view_v2.py class PhotoListWithoutTimestampTest (line 11) | class PhotoListWithoutTimestampTest(TestCase): method setUp (line 12) | def setUp(self): method test_download (line 18) | def test_download(self, patched_shutil): FILE: api/tests/utils.py function create_password (line 19) | def create_password(): function create_user_details (line 23) | def create_user_details(is_admin=False): function create_test_person (line 34) | def create_test_person( function create_test_face (line 55) | def create_test_face( function create_test_user (line 94) | def create_test_user(is_admin=False, public_sharing=False, **kwargs): function create_test_photo (line 112) | def create_test_photo(**kwargs): function create_test_photos (line 213) | def create_test_photos(number_of_photos=1, **kwargs): function create_test_photos_with_faces (line 217) | def create_test_photos_with_faces(number_of_photos=1, **kwargs): function create_test_file (line 223) | def create_test_file(path: str, user: User, content: bytes): function share_test_photos (line 229) | def share_test_photos(photo_ids, user): FILE: api/thumbnails.py function create_thumbnail (line 12) | def create_thumbnail(input_path, output_height, output_path, hash, file_... function create_animated_thumbnail (line 56) | def create_animated_thumbnail(input_path, output_height, output_path, ha... function create_thumbnail_for_video (line 84) | def create_thumbnail_for_video(input_path, output_path, hash, file_type): function does_static_thumbnail_exist (line 107) | def does_static_thumbnail_exist(output_path, hash): function does_video_thumbnail_exist (line 113) | def does_video_thumbnail_exist(output_path, hash): FILE: api/util.py function is_valid_path (line 22) | def is_valid_path(path, root_path): function is_number (line 43) | def is_number(s): function convert_to_degrees (line 51) | def convert_to_degrees(values): FILE: api/views/album_auto.py class AlbumAutoViewSet (line 20) | class AlbumAutoViewSet(viewsets.ModelViewSet): method get_queryset (line 24) | def get_queryset(self): method delete_all (line 76) | def delete_all(self, request): class AlbumAutoListViewSet (line 82) | class AlbumAutoListViewSet(ListViewSet): method get_queryset (line 92) | def get_queryset(self): class RegenerateAutoAlbumTitles (line 110) | class RegenerateAutoAlbumTitles(APIView): method get (line 115) | def get(self, request, format=None): method post (line 118) | def post(self, request, format=None): method _schedule_auto_album_title_regeneration (line 121) | def _schedule_auto_album_title_regeneration(self, request, format=None): class AutoAlbumGenerateView (line 131) | class AutoAlbumGenerateView(APIView): method get (line 136) | def get(self, request, format=None): method post (line 139) | def post(self, request, format=None): method _schedule_auto_album_regeneration (line 142) | def _schedule_auto_album_regeneration(self, request): FILE: api/views/album_folder.py class FolderNavigationViewSet (line 13) | class FolderNavigationViewSet(viewsets.ViewSet): method subfolders (line 32) | def subfolders(self, request): FILE: api/views/albums.py class AlbumPersonViewSet (line 56) | class AlbumPersonViewSet(viewsets.ModelViewSet): method get_queryset (line 59) | def get_queryset(self): method retrieve (line 89) | def retrieve(self, *args, **kwargs): method list (line 98) | def list(self, *args, **kwargs): class PersonViewSet (line 106) | class PersonViewSet(viewsets.ModelViewSet): method get_queryset (line 113) | def get_queryset(self): function _get_active_tag_thing_types (line 134) | def _get_active_tag_thing_types(): class AlbumThingViewSet (line 144) | class AlbumThingViewSet(viewsets.ModelViewSet): method get_queryset (line 148) | def get_queryset(self): method retrieve (line 171) | def retrieve(self, *args, **kwargs): method list (line 180) | def list(self, *args, **kwargs): class AlbumThingListViewSet (line 189) | class AlbumThingListViewSet(ListViewSet): method get_queryset (line 195) | def get_queryset(self): class AlbumPlaceViewSet (line 211) | class AlbumPlaceViewSet(viewsets.ModelViewSet): method get_queryset (line 215) | def get_queryset(self): method retrieve (line 236) | def retrieve(self, *args, **kwargs): class AlbumPlaceListViewSet (line 247) | class AlbumPlaceListViewSet(ListViewSet): method get_queryset (line 253) | def get_queryset(self): class AlbumUserViewSet (line 277) | class AlbumUserViewSet(viewsets.ModelViewSet): method perform_create (line 281) | def perform_create(self, serializer): method get_queryset (line 284) | def get_queryset(self): method get_permissions (line 313) | def get_permissions(self): method get_serializer_class (line 320) | def get_serializer_class(self): class AlbumUserListViewSet (line 327) | class AlbumUserListViewSet(ListViewSet): method get_queryset (line 333) | def get_queryset(self): class AlbumDateViewSet (line 348) | class AlbumDateViewSet(viewsets.ModelViewSet): method get_queryset (line 352) | def get_queryset(self): method get_permissions (line 482) | def get_permissions(self): method retrieve (line 502) | def retrieve(self, *args, **kwargs): class AlbumDateListViewSet (line 514) | class AlbumDateListViewSet(ListViewSet): method get_queryset (line 524) | def get_queryset(self): method get_permissions (line 601) | def get_permissions(self): method list (line 621) | def list(self, *args, **kwargs): FILE: api/views/custom_api_view.py class ListViewSet (line 4) | class ListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): FILE: api/views/dataviz.py class ClusterFaceView (line 23) | class ClusterFaceView(APIView): method get (line 28) | def get(self, request, format=None): method post (line 31) | def post(self, request, format=None): method _cluster_faces (line 34) | def _cluster_faces(self, user): class SocialGraphView (line 39) | class SocialGraphView(APIView): method get (line 40) | def get(self, request, format=None): class ServerLogsView (line 49) | class ServerLogsView(APIView): method get (line 50) | def get(self, request, format=None): class ServerStatsView (line 65) | class ServerStatsView(APIView): method get (line 66) | def get(self, request, format=None): class StatsView (line 73) | class StatsView(APIView): method get (line 74) | def get(self, request, format=None): class LocationClustersView (line 79) | class LocationClustersView(APIView): method get (line 80) | def get(self, request, format=None): class LocationSunburst (line 85) | class LocationSunburst(APIView): method get (line 86) | def get(self, request, format=None): class LocationTimeline (line 91) | class LocationTimeline(APIView): method get (line 92) | def get(self, request, format=None): class PhotoMonthCountsView (line 97) | class PhotoMonthCountsView(APIView): method get (line 98) | def get(self, request, format=None): class SearchTermWordCloudView (line 103) | class SearchTermWordCloudView(APIView): method get (line 104) | def get(self, request, format=None): FILE: api/views/duplicates.py class DuplicateListView (line 27) | class DuplicateListView(APIView): method get (line 56) | def get(self, request): class DuplicateDetailView (line 132) | class DuplicateDetailView(APIView): method get (line 137) | def get(self, request, duplicate_id): class DuplicateResolveView (line 209) | class DuplicateResolveView(APIView): method post (line 214) | def post(self, request, duplicate_id): class DuplicateDismissView (line 255) | class DuplicateDismissView(APIView): method post (line 260) | def post(self, request, duplicate_id): class DuplicateRevertView (line 274) | class DuplicateRevertView(APIView): method post (line 279) | def post(self, request, duplicate_id): class DuplicateDeleteView (line 301) | class DuplicateDeleteView(APIView): method delete (line 306) | def delete(self, request, duplicate_id): class DetectDuplicatesView (line 329) | class DetectDuplicatesView(APIView): method post (line 363) | def post(self, request): class DuplicateStatsView (line 399) | class DuplicateStatsView(APIView): method get (line 404) | def get(self, request): FILE: api/views/faces.py class ScanFacesView (line 27) | class ScanFacesView(APIView): method get (line 32) | def get(self, request, format=None): method post (line 35) | def post(self, request, format=None): method _scan_faces (line 38) | def _scan_faces(self, request, format=None): class TrainFaceView (line 52) | class TrainFaceView(APIView): method _train_faces (line 54) | def _train_faces(request): method post (line 68) | def post(self, request, format=None): class FaceListView (line 72) | class FaceListView(ListViewSet): method get_queryset (line 76) | def get_queryset(self): method list (line 138) | def list(self, *args, **kwargs): class FaceIncompleteListViewSet (line 142) | class FaceIncompleteListViewSet(ListViewSet): method get_queryset (line 146) | def get_queryset(self): method list (line 204) | def list(self, *args, **kwargs): class SetFacePersonLabel (line 250) | class SetFacePersonLabel(APIView): method post (line 251) | def post(self, request, format=None): class DeleteFaces (line 311) | class DeleteFaces(APIView): method post (line 312) | def post(self, request, format=None): FILE: api/views/geocode.py class GeocodeSearchView (line 7) | class GeocodeSearchView(APIView): method get (line 10) | def get(self, request, format=None): FILE: api/views/jobs.py class LongRunningJobViewSet (line 11) | class LongRunningJobViewSet(viewsets.ModelViewSet): class QueueAvailabilityView (line 26) | class QueueAvailabilityView(APIView): method get (line 27) | def get(self, request, format=None): FILE: api/views/pagination.py class HugeResultsSetPagination (line 4) | class HugeResultsSetPagination(PageNumberPagination): class StandardResultsSetPagination (line 10) | class StandardResultsSetPagination(PageNumberPagination): class RegularResultsSetPagination (line 16) | class RegularResultsSetPagination(PageNumberPagination): class TinyResultsSetPagination (line 22) | class TinyResultsSetPagination(PageNumberPagination): FILE: api/views/photo_filters.py function build_photo_queryset (line 13) | def build_photo_queryset(user, params: dict): FILE: api/views/photo_metadata.py class PhotoMetadataViewSet (line 28) | class PhotoMetadataViewSet(ViewSet): method _get_photo (line 41) | def _get_photo(self, request, photo_id: str) -> Photo: method _get_or_create_metadata (line 60) | def _get_or_create_metadata(self, photo: Photo) -> PhotoMetadata: method retrieve (line 79) | def retrieve(self, request, photo_id: str): method partial_update (line 91) | def partial_update(self, request, photo_id: str): method history (line 113) | def history(self, request, photo_id: str): method revert (line 137) | def revert(self, request, photo_id: str, edit_id: str): method revert_all (line 171) | def revert_all(self, request, photo_id: str): class BulkMetadataView (line 203) | class BulkMetadataView(APIView): method get (line 216) | def get(self, request): method patch (line 278) | def patch(self, request): FILE: api/views/photos.py class RecentlyAddedPhotoListViewSet (line 30) | class RecentlyAddedPhotoListViewSet(ListViewSet): method get_queryset (line 34) | def get_queryset(self): method list (line 94) | def list(self, *args, **kwargs): method _get_latest_photo (line 101) | def _get_latest_photo(self): class NoTimestampPhotoViewSet (line 112) | class NoTimestampPhotoViewSet(ListViewSet): method get_queryset (line 122) | def get_queryset(self): method list (line 157) | def list(self, *args, **kwargs): class SetPhotosDeleted (line 161) | class SetPhotosDeleted(APIView): method post (line 162) | def post(self, request, format=None): class SetPhotosFavorite (line 288) | class SetPhotosFavorite(APIView): method post (line 289) | def post(self, request, format=None): class SetPhotosHidden (line 393) | class SetPhotosHidden(APIView): method post (line 394) | def post(self, request, format=None): class PhotoViewSet (line 481) | class PhotoViewSet(viewsets.ModelViewSet): method get_object (line 493) | def get_object(self): method summary (line 538) | def summary(self, request, pk): method albums (line 577) | def albums(self, request, pk): method get_permissions (line 596) | def get_permissions(self): method get_queryset (line 606) | def get_queryset(self): method retrieve (line 629) | def retrieve(self, *args, **kwargs): method list (line 632) | def list(self, *args, **kwargs): # pragma: no cover - unused class PhotoEditViewSet (line 636) | class PhotoEditViewSet(viewsets.ModelViewSet): method get_queryset (line 640) | def get_queryset(self): method get_object (line 643) | def get_object(self): method retrieve (line 677) | def retrieve( method list (line 682) | def list( class SetPhotosShared (line 688) | class SetPhotosShared(APIView): method post (line 689) | def post(self, request, format=None): class SetPhotosPublic (line 761) | class SetPhotosPublic(APIView): method post (line 762) | def post(self, request, format=None): class GeneratePhotoCaption (line 850) | class GeneratePhotoCaption(APIView): method post (line 853) | def post(self, request, format=None): class SavePhotoCaption (line 879) | class SavePhotoCaption(APIView): method post (line 882) | def post(self, request, format=None): class DeletePhotos (line 899) | class DeletePhotos(APIView): method delete (line 900) | def delete(self, request): class FileVariantDownloadView (line 959) | class FileVariantDownloadView(APIView): method get (line 976) | def get(self, request, image_hash, file_hash): class SetMainFileView (line 1041) | class SetMainFileView(APIView): method post (line 1049) | def post(self, request, image_hash): class SaveMetadataView (line 1100) | class SaveMetadataView(APIView): method post (line 1101) | def post(self, request, format=None): FILE: api/views/public_albums.py class SetUserAlbumPublic (line 18) | class SetUserAlbumPublic(APIView): method post (line 19) | def post(self, request, format=None): class PublicAlbumBySlug (line 75) | class PublicAlbumBySlug(APIView): method get (line 82) | def get(self, request, slug): class PublicPhotoDetailBySlug (line 105) | class PublicPhotoDetailBySlug(APIView): method get (line 117) | def get(self, request, slug, photo_id): FILE: api/views/search.py class SearchListViewSet (line 12) | class SearchListViewSet(ListViewSet): method get_queryset (line 23) | def get_queryset(self): method list (line 28) | def list(self, request): FILE: api/views/services.py class ServiceViewSet (line 9) | class ServiceViewSet(viewsets.ViewSet): method list (line 12) | def list(self, request): method retrieve (line 15) | def retrieve(self, request, pk=None): method start (line 28) | def start(self, request, pk=None): method stop (line 50) | def stop(self, request, pk=None): FILE: api/views/sharing.py class SharedToMePhotoSuperSimpleListViewSet (line 13) | class SharedToMePhotoSuperSimpleListViewSet(ListViewSet): method get_queryset (line 17) | def get_queryset(self): class SharedFromMePhotoSuperSimpleListViewSet (line 33) | class SharedFromMePhotoSuperSimpleListViewSet(ListViewSet): method get_queryset (line 37) | def get_queryset(self): class SharedToMeAlbumUserListViewSet (line 66) | class SharedToMeAlbumUserListViewSet(ListViewSet): method get_queryset (line 70) | def get_queryset(self): class SharedFromMeAlbumUserListViewSet (line 74) | class SharedFromMeAlbumUserListViewSet(ListViewSet): method get_queryset (line 78) | def get_queryset(self): FILE: api/views/stacks.py class PhotoStackListView (line 29) | class PhotoStackListView(APIView): method get (line 53) | def get(self, request): class PhotoStackDetailView (line 128) | class PhotoStackDetailView(APIView): method get (line 133) | def get(self, request, stack_id): method delete (line 204) | def delete(self, request, stack_id): class PhotoStackDeleteView (line 229) | class PhotoStackDeleteView(APIView): method delete (line 234) | def delete(self, request, stack_id): class PhotoStackSetPrimaryView (line 258) | class PhotoStackSetPrimaryView(APIView): method post (line 263) | def post(self, request, stack_id): class DetectStacksView (line 296) | class DetectStacksView(APIView): method post (line 316) | def post(self, request): class PhotoStackStatsView (line 339) | class PhotoStackStatsView(APIView): method get (line 344) | def get(self, request): class CreateManualStackView (line 379) | class CreateManualStackView(APIView): method post (line 384) | def post(self, request): class AddToStackView (line 441) | class AddToStackView(APIView): method post (line 446) | def post(self, request, stack_id): class RemoveFromStackView (line 480) | class RemoveFromStackView(APIView): method post (line 485) | def post(self, request, stack_id): class MergeStacksView (line 534) | class MergeStacksView(APIView): method post (line 539) | def post(self, request): FILE: api/views/timezone.py class TimeZoneView (line 7) | class TimeZoneView(APIView): method get (line 8) | def get(self, request, format=None): FILE: api/views/upload.py function generate_captions_wrapper (line 27) | def generate_captions_wrapper(photo, commit=True): class UploadPhotoExists (line 33) | class UploadPhotoExists(viewsets.ViewSet): method retrieve (line 34) | def retrieve(self, request, pk): class UploadPhotosChunked (line 46) | class UploadPhotosChunked(ChunkedUploadView): method check_permissions (line 49) | def check_permissions(self, request): method create_chunked_upload (line 77) | def create_chunked_upload(self, save=False, **attrs): class UploadPhotosChunkedComplete (line 88) | class UploadPhotosChunkedComplete(ChunkedUploadCompleteView): method check_permissions (line 91) | def check_permissions(self, request): method on_completion (line 118) | def on_completion(self, uploaded_file, request): FILE: api/views/user.py class DefaultRulesView (line 25) | class DefaultRulesView(APIView): method get (line 26) | def get(self, request, format=None): class PredefinedRulesView (line 31) | class PredefinedRulesView(APIView): method get (line 32) | def get(self, request, format=None): class DefaultBurstRulesView (line 37) | class DefaultBurstRulesView(APIView): method get (line 39) | def get(self, request, format=None): class PredefinedBurstRulesView (line 44) | class PredefinedBurstRulesView(APIView): method get (line 46) | def get(self, request, format=None): class RootPathTreeView (line 51) | class RootPathTreeView(APIView): method get (line 54) | def get(self, request, format=None): class IsFirstTimeSetupView (line 79) | class IsFirstTimeSetupView(APIView): method get (line 82) | def get(self, request): class UserViewSet (line 89) | class UserViewSet(viewsets.ModelViewSet): method get_queryset (line 90) | def get_queryset(self): method get_serializer_class (line 123) | def get_serializer_class(self): method get_permissions (line 132) | def get_permissions(self): class DeleteUserViewSet (line 143) | class DeleteUserViewSet(viewsets.ModelViewSet): method destroy (line 148) | def destroy(self, request, *args, **kwargs): class ManageUserViewSet (line 159) | class ManageUserViewSet(viewsets.ModelViewSet): method retrieve (line 164) | def retrieve(self, *args, **kwargs): method list (line 167) | def list(self, *args, **kwargs): FILE: api/views/views.py function custom_exception_handler (line 43) | def custom_exception_handler(exc, context): class AlbumUserEditViewSet (line 81) | class AlbumUserEditViewSet(viewsets.ModelViewSet): method retrieve (line 85) | def retrieve(self, *args, **kwargs): method list (line 88) | def list(self, *args, **kwargs): method get_queryset (line 91) | def get_queryset(self): method get_permissions (line 96) | def get_permissions(self): class SiteSettingsView (line 106) | class SiteSettingsView(APIView): method get_permissions (line 107) | def get_permissions(self): method get (line 115) | def get(self, request, format=None): method post (line 128) | def post(self, request, format=None): class SetUserAlbumShared (line 152) | class SetUserAlbumShared(APIView): method post (line 153) | def post(self, request, format=None): class StorageStatsView (line 206) | class StorageStatsView(APIView): method get (line 207) | def get(self, request, format=None): class ApiHelpView (line 222) | class ApiHelpView(APIView): method get (line 225) | def get(self, request, format=None): class ImageTagView (line 295) | class ImageTagView(APIView): method get (line 297) | def get(self, request, format=None): class SearchTermExamples (line 315) | class SearchTermExamples(APIView): method get (line 318) | def get(self, request, format=None): class ScanPhotosView (line 324) | class ScanPhotosView(APIView): method post (line 325) | def post(self, request, format=None): method get (line 332) | def get(self, request, format=None): method _scan_photos (line 335) | def _scan_photos(self, request): class SelectiveScanPhotosView (line 372) | class SelectiveScanPhotosView(APIView): method get (line 373) | def get(self, request, format=None): class FullScanPhotosView (line 414) | class FullScanPhotosView(APIView): method post (line 415) | def post(self, request, format=None): method get (line 422) | def get(self, request, format=None): method _scan_photos (line 425) | def _scan_photos(self, request): class DeleteMissingPhotosView (line 441) | class DeleteMissingPhotosView(APIView): method post (line 442) | def post(self, request, format=None): method get (line 449) | def get(self, request, format=None): method _delete_missing_photos (line 452) | def _delete_missing_photos(self, request, format=None): class MediaAccessView (line 462) | class MediaAccessView(APIView): method _get_protected_media_url (line 465) | def _get_protected_media_url(self, path, fname): method get (line 469) | def get(self, request, path, fname, format=None): class VideoTranscoder (line 526) | class VideoTranscoder: method __init__ (line 529) | def __init__(self, path): method __del__ (line 552) | def __del__(self): function gen (line 556) | def gen(transcoder): class UnifiedMediaAccessView (line 560) | class UnifiedMediaAccessView(APIView): method _should_use_proxy (line 568) | def _should_use_proxy(self): method _protected_media_url (line 571) | def _protected_media_url(self, path, fname): method _serve_file_direct (line 575) | def _serve_file_direct(self, file_path, content_type=None): method _generate_response_proxy (line 594) | def _generate_response_proxy(self, photo, path, fname, transcode_videos): method _generate_response_direct (line 656) | def _generate_response_direct(self, photo, path, fname, transcode_vide... method _public_album_active_q (line 686) | def _public_album_active_q(self): method get (line 691) | def get(self, request, path, fname, album_id=None, format=None): class ZipListPhotosView_V2 (line 998) | class ZipListPhotosView_V2(APIView): method post (line 999) | def post(self, request): method get (line 1058) | def get(self, request): class DeleteZipView (line 1080) | class DeleteZipView(APIView): method delete (line 1081) | def delete(self, request, fname): FILE: image_similarity/main.py class BuildIndex (line 15) | class BuildIndex(Resource): method post (line 16) | def post(self): method delete (line 29) | def delete(self): class SearchIndex (line 38) | class SearchIndex(Resource): method post (line 39) | def post(self): class Health (line 63) | class Health(Resource): method get (line 64) | def get(self): function start_server (line 73) | def start_server(): FILE: image_similarity/retrieval_index.py class RetrievalIndex (line 10) | class RetrievalIndex: method __init__ (line 11) | def __init__(self): method build_index_for_user (line 15) | def build_index_for_user(self, user_id, image_hashes, image_embeddings): method search_similar (line 71) | def search_similar(self, user_id, in_embedding, n=100, thres=27.0): FILE: librephotos/urls.py class CustomTokenObtainPairSerializer (line 61) | class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): method get_token (line 63) | def get_token(cls, user): class CustomTokenObtainPairView (line 79) | class CustomTokenObtainPairView(TokenObtainPairView): method post (line 82) | def post(self, request, *args, **kwargs): class CustomTokenRefreshView (line 89) | class CustomTokenRefreshView(TokenRefreshView): method post (line 92) | def post(self, request, *args, **kwargs): class FrontendView (line 99) | class FrontendView(TemplateView): method get (line 104) | def get(self, request, *args, **kwargs): FILE: nextcloud/apps.py class NextcloudConfig (line 4) | class NextcloudConfig(AppConfig): FILE: nextcloud/directory_watcher.py function isValidNCMedia (line 13) | def isValidNCMedia(file_obj): function collect_photos (line 30) | def collect_photos(nc, path, photos): function scan_photos (line 38) | def scan_photos(user, job_id): FILE: nextcloud/views.py class ListDir (line 14) | class ListDir(APIView): method get (line 15) | def get(self, request, format=None): function valid_url (line 43) | def valid_url(url): class ScanPhotosView (line 51) | class ScanPhotosView(APIView): method post (line 52) | def post(self, request, format=None): method get (line 59) | def get(self, request, format=None): method _scan_photos (line 62) | def _scan_photos(self, request): FILE: service/clip_embeddings/main.py function log (line 11) | def log(message): function create_clip_embeddings (line 20) | def create_clip_embeddings(): function calculate_query_embeddings (line 48) | def calculate_query_embeddings(): function health (line 70) | def health(): FILE: service/clip_embeddings/semantic_search/semantic_search.py class SemanticSearch (line 8) | class SemanticSearch: method load (line 12) | def load(self, model): method unload (line 17) | def unload(self): method load_model (line 24) | def load_model(self, model): method calculate_clip_embeddings (line 27) | def calculate_clip_embeddings(self, img_paths, model): method calculate_query_embeddings (line 74) | def calculate_query_embeddings(self, query, model): FILE: service/exif/main.py function log (line 12) | def log(message): function get_tags (line 17) | def get_tags(): function health (line 50) | def health(): FILE: service/face_recognition/main.py function log (line 15) | def log(message): function create_face_encodings (line 20) | def create_face_encodings(): function create_face_locations (line 45) | def create_face_locations(): function health (line 64) | def health(): FILE: service/image_captioning/api/im2txt/blip/blip.py class BLIP_Base (line 19) | class BLIP_Base(nn.Module): method __init__ (line 20) | def __init__( method forward (line 44) | def forward(self, image, caption, mode): class BLIP_Decoder (line 85) | class BLIP_Decoder(nn.Module): method __init__ (line 86) | def __init__( method forward (line 114) | def forward(self, image, caption): method generate (line 147) | def generate( function blip_decoder (line 211) | def blip_decoder(pretrained="", **kwargs): function blip_feature_extractor (line 219) | def blip_feature_extractor(pretrained="", **kwargs): function init_tokenizer (line 227) | def init_tokenizer(): function create_vit (line 235) | def create_vit(vit, image_size, ckpt_layer=0, drop_path_rate=0): function is_url (line 262) | def is_url(url_or_filename): function load_checkpoint (line 267) | def load_checkpoint(model, url_or_filename): FILE: service/image_captioning/api/im2txt/blip/med.py class BertEmbeddings (line 43) | class BertEmbeddings(nn.Module): method __init__ (line 46) | def __init__(self, config): method forward (line 70) | def forward( class BertSelfAttention (line 102) | class BertSelfAttention(nn.Module): method __init__ (line 103) | def __init__(self, config, is_cross_attention): method save_attn_gradients (line 140) | def save_attn_gradients(self, attn_gradients): method get_attn_gradients (line 143) | def get_attn_gradients(self): method save_attention_map (line 146) | def save_attention_map(self, attention_map): method get_attention_map (line 149) | def get_attention_map(self): method transpose_for_scores (line 152) | def transpose_for_scores(self, x): method forward (line 160) | def forward( class BertSelfOutput (line 268) | class BertSelfOutput(nn.Module): method __init__ (line 269) | def __init__(self, config): method forward (line 275) | def forward(self, hidden_states, input_tensor): class BertAttention (line 282) | class BertAttention(nn.Module): method __init__ (line 283) | def __init__(self, config, is_cross_attention=False): method prune_heads (line 289) | def prune_heads(self, heads): method forward (line 312) | def forward( class BertIntermediate (line 338) | class BertIntermediate(nn.Module): method __init__ (line 339) | def __init__(self, config): method forward (line 347) | def forward(self, hidden_states): class BertOutput (line 353) | class BertOutput(nn.Module): method __init__ (line 354) | def __init__(self, config): method forward (line 360) | def forward(self, hidden_states, input_tensor): class BertLayer (line 367) | class BertLayer(nn.Module): method __init__ (line 368) | def __init__(self, config, layer_num): method forward (line 382) | def forward( method feed_forward_chunk (line 438) | def feed_forward_chunk(self, attention_output): class BertEncoder (line 444) | class BertEncoder(nn.Module): method __init__ (line 445) | def __init__(self, config): method forward (line 453) | def forward( class BertPooler (line 547) | class BertPooler(nn.Module): method __init__ (line 548) | def __init__(self, config): method forward (line 553) | def forward(self, hidden_states): class BertPredictionHeadTransform (line 562) | class BertPredictionHeadTransform(nn.Module): method __init__ (line 563) | def __init__(self, config): method forward (line 572) | def forward(self, hidden_states): class BertLMPredictionHead (line 579) | class BertLMPredictionHead(nn.Module): method __init__ (line 580) | def __init__(self, config): method forward (line 593) | def forward(self, hidden_states): class BertOnlyMLMHead (line 599) | class BertOnlyMLMHead(nn.Module): method __init__ (line 600) | def __init__(self, config): method forward (line 604) | def forward(self, sequence_output): class BertPreTrainedModel (line 609) | class BertPreTrainedModel(PreTrainedModel): method _init_weights (line 618) | def _init_weights(self, module): class BertModel (line 631) | class BertModel(BertPreTrainedModel): method __init__ (line 640) | def __init__(self, config, add_pooling_layer=True): method get_input_embeddings (line 652) | def get_input_embeddings(self): method set_input_embeddings (line 655) | def set_input_embeddings(self, value): method _prune_heads (line 658) | def _prune_heads(self, heads_to_prune): method get_extended_attention_mask (line 665) | def get_extended_attention_mask( method forward (line 741) | def forward( class BertLMHeadModel (line 911) | class BertLMHeadModel(BertPreTrainedModel): method __init__ (line 915) | def __init__(self, config): method get_output_embeddings (line 923) | def get_output_embeddings(self): method set_output_embeddings (line 926) | def set_output_embeddings(self, new_embeddings): method forward (line 929) | def forward( method prepare_inputs_for_generation (line 1037) | def prepare_inputs_for_generation( method _reorder_cache (line 1058) | def _reorder_cache(self, past, beam_idx): FILE: service/image_captioning/api/im2txt/blip/vit.py class Mlp (line 19) | class Mlp(nn.Module): method __init__ (line 22) | def __init__( method forward (line 38) | def forward(self, x): class Attention (line 47) | class Attention(nn.Module): method __init__ (line 48) | def __init__( method save_attn_gradients (line 69) | def save_attn_gradients(self, attn_gradients): method get_attn_gradients (line 72) | def get_attn_gradients(self): method save_attention_map (line 75) | def save_attention_map(self, attention_map): method get_attention_map (line 78) | def get_attention_map(self): method forward (line 81) | def forward(self, x, register_hook=False): class Block (line 108) | class Block(nn.Module): method __init__ (line 109) | def __init__( method forward (line 143) | def forward(self, x, register_hook=False): class VisionTransformer (line 149) | class VisionTransformer(nn.Module): method __init__ (line 155) | def __init__( method _init_weights (line 236) | def _init_weights(self, m): method no_weight_decay (line 246) | def no_weight_decay(self): method forward (line 249) | def forward(self, x, register_blk=-1): method load_pretrained (line 268) | def load_pretrained(self, checkpoint_path, prefix=""): function _load_weights (line 273) | def _load_weights(model: VisionTransformer, checkpoint_path: str, prefix... function interpolate_pos_embed (line 380) | def interpolate_pos_embed(pos_embed_checkpoint, visual_encoder): FILE: service/image_captioning/api/im2txt/build_vocab.py class Vocabulary (line 11) | class Vocabulary: method __init__ (line 14) | def __init__(self): method add_word (line 19) | def add_word(self, word): method __call__ (line 25) | def __call__(self, word): method __len__ (line 30) | def __len__(self): function build_vocab (line 34) | def build_vocab(json, threshold): function main (line 66) | def main(): FILE: service/image_captioning/api/im2txt/data_loader.py class CocoDataset (line 8) | class CocoDataset(data.Dataset): method __init__ (line 11) | def __init__(self, root, json, vocab, transform=None): method __getitem__ (line 30) | def __getitem__(self, index): method __len__ (line 55) | def __len__(self): function collate_fn (line 59) | def collate_fn(data): function get_loader (line 92) | def get_loader(root, json, vocab, transform, batch_size, shuffle, num_wo... FILE: service/image_captioning/api/im2txt/model.py class EncoderCNN (line 6) | class EncoderCNN(nn.Module): method __init__ (line 7) | def __init__(self, embed_size): method forward (line 16) | def forward(self, images): class DecoderRNN (line 25) | class DecoderRNN(nn.Module): method __init__ (line 26) | def __init__( method forward (line 36) | def forward(self, features): FILE: service/image_captioning/api/im2txt/resize.py function resize_image (line 10) | def resize_image(image, size): function resize_images (line 15) | def resize_images(image_dir, output_dir, size): function main (line 33) | def main(): FILE: service/image_captioning/api/im2txt/sample.py class Im2txt (line 33) | class Im2txt: method __init__ (line 34) | def __init__( method load_image (line 47) | def load_image(self, image_path, transform=None): method load_models (line 59) | def load_models(self, onnx=False): method unload_models (line 98) | def unload_models(self): method generate_caption (line 106) | def generate_caption( FILE: service/image_captioning/api/im2txt/train.py function main (line 33) | def main(): FILE: service/image_captioning/main.py function log (line 15) | def log(message): function generate_caption (line 20) | def generate_caption(): function unload_model (line 45) | def unload_model(): function health (line 53) | def health(): FILE: service/llm/main.py function log (line 28) | def log(message): function load_model (line 32) | def load_model(model_path, multimodal=False): function generate (line 70) | def generate(): function health (line 130) | def health(): FILE: service/tags/main.py function log (line 16) | def log(message): function generate_tags (line 21) | def generate_tags(): function health (line 51) | def health(): FILE: service/tags/places365/places365.py class Places365 (line 22) | class Places365: method unload (line 25) | def unload(self): method load (line 39) | def load(self): method load_model (line 44) | def load_model(self): method load_labels (line 66) | def load_labels(self): method returnTF (line 102) | def returnTF(self): method remove_nonspace_separators (line 113) | def remove_nonspace_separators(self, text): method inference_places365 (line 116) | def inference_places365(self, img_path, confidence): FILE: service/tags/places365/wideresnet.py function conv3x3 (line 11) | def conv3x3(in_planes, out_planes, stride=1): class BasicBlock (line 18) | class BasicBlock(nn.Module): method __init__ (line 21) | def __init__(self, inplanes, planes, stride=1, downsample=None): method forward (line 31) | def forward(self, x): class ResNet (line 50) | class ResNet(nn.Module): method __init__ (line 51) | def __init__(self, block, layers, num_classes=1000): method _make_layer (line 73) | def _make_layer(self, block, planes, blocks, stride=1): method forward (line 95) | def forward(self, x): function resnet18 (line 113) | def resnet18(pretrained=False, **kwargs): FILE: service/tags/siglip2/siglip2.py function _pool_embeddings (line 25) | def _pool_embeddings(raw_output, attention_mask=None): function _l2_normalize (line 49) | def _l2_normalize(embeddings): class SigLIP2 (line 56) | class SigLIP2: method __init__ (line 57) | def __init__(self): method load (line 64) | def load(self): method unload (line 107) | def unload(self): method _load_tokenizer (line 117) | def _load_tokenizer(self): method _tokenize (line 122) | def _tokenize(self, texts, max_length=MAX_TOKEN_LENGTH): method _build_tag_embeddings (line 151) | def _build_tag_embeddings(self): method prepare_image (line 218) | def prepare_image(self, image): method predict (line 230) | def predict(self, image_path, threshold=0.05, max_tags=10): FILE: service/thumbnail/main.py function log (line 9) | def log(message): function create_thumbnail (line 14) | def create_thumbnail(): function health (line 35) | def health(): FILE: service/thumbnail/test/test_thumbnail_worker.py function client (line 12) | def client(): function test_must_fail_when_passing_empty_string (line 16) | def test_must_fail_when_passing_empty_string(client): function test_must_fail_when_passing_invalid_json (line 21) | def test_must_fail_when_passing_invalid_json(client): function test_must_fail_when_passing_incomplete_json (line 26) | def test_must_fail_when_passing_incomplete_json(client): function test_should_create_thumbnail (line 40) | def test_should_create_thumbnail(client):