pointblank 0.9.0__py3-none-any.whl → 0.9.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pointblank/_constants.py CHANGED
@@ -39,10 +39,12 @@ ASSERTION_TYPE_METHOD_MAP = {
39
39
  "col_vals_expr": "expr",
40
40
  "col_exists": "col_exists",
41
41
  "rows_distinct": "rows_distinct",
42
+ "rows_complete": "rows_complete",
42
43
  "col_schema_match": "col_schema_match",
43
44
  "row_count_match": "row_count_match",
44
45
  "col_count_match": "col_count_match",
45
46
  "conjointly": "conjointly",
47
+ "specially": "specially",
46
48
  }
47
49
 
48
50
  METHOD_CATEGORY_MAP = {
@@ -63,10 +65,12 @@ METHOD_CATEGORY_MAP = {
63
65
  "col_exists": "COL_EXISTS_HAS_TYPE",
64
66
  "expr": "COMPARE_EXPR",
65
67
  "rows_distinct": "ROWS_DISTINCT",
68
+ "rows_complete": "ROWS_COMPLETE",
66
69
  "col_schema_match": "COL_SCHEMA_MATCH",
67
70
  "row_count_match": "ROW_COUNT_MATCH",
68
71
  "col_count_match": "COL_COUNT_MATCH",
69
72
  "conjointly": "CONJOINTLY",
73
+ "specially": "SPECIALLY",
70
74
  }
71
75
 
72
76
  COMPARISON_OPERATORS = {
@@ -375,6 +379,19 @@ SVG_ICONS_FOR_ASSERTION_TYPES = {
375
379
  </g>
376
380
  </g>
377
381
  </g>
382
+ </svg>""",
383
+ "rows_complete": """<?xml version="1.0" encoding="UTF-8"?>
384
+ <svg width="67px" height="67px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
385
+ <title>rows_complete</title>
386
+ <g id="All-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
387
+ <g id="rows_complete" transform="translate(0.000000, 0.965517)">
388
+ <path d="M56.712234,1 C59.1975153,1 61.4475153,2.00735931 63.076195,3.63603897 C64.7048747,5.26471863 65.712234,7.51471863 65.712234,10 L65.712234,10 L65.712234,65 L10.712234,65 C8.22695259,65 5.97695259,63.9926407 4.34827294,62.363961 C2.71959328,60.7352814 1.71223397,58.4852814 1.71223397,56 L1.71223397,56 L1.71223397,10 C1.71223397,7.51471863 2.71959328,5.26471863 4.34827294,3.63603897 C5.97695259,2.00735931 8.22695259,1 10.712234,1 L10.712234,1 Z" id="rectangle" stroke="#000000" stroke-width="2" fill="#FFFFFF"></path>
389
+ <g id="complete_me" transform="translate(12.500000, 9.500000)" fill="#000000">
390
+ <path d="M8,0 L8,10 L16,10 L16,18 L26,18 L26,10 L34,10 L34,0 L8,0 Z M10,2 L16,2 L16,8 L10,8 L10,2 Z M18,2 L24,2 L24,8 L18,8 L18,2 Z M26,2 L32,2 L32,8 L26,8 L26,2 Z M18,10 L24,10 L24,16 L18,16 L18,10 Z M0,21 L0,47 L42,47 L42,21 L32,21 L32,29 L24,29 L24,37 L18,37 L18,29 L10,29 L10,21 L0,21 Z M2,23 L8,23 L8,29 L2,29 L2,23 Z M34,23 L40,23 L40,29 L34,29 L34,23 Z M2,31 L8,31 L8,37 L2,37 L2,31 Z M10,31 L16,31 L16,37 L10,37 L10,31 Z M26,31 L32,31 L32,37 L26,37 L26,31 Z M34,31 L40,31 L40,37 L34,37 L34,31 Z M2,39 L8,39 L8,45 L2,45 L2,39 Z M10,39 L16,39 L16,45 L10,45 L10,39 Z M18,39 L24,39 L24,45 L18,45 L18,39 Z M26,39 L32,39 L32,45 L26,45 L26,39 Z M34,39 L40,39 L40,45 L34,45 L34,39 Z" id="Shape" fill-rule="nonzero"></path>
391
+ <path d="M22.4566476,18.35817 C22.9253976,18.29567 23.3746166,18.569108 23.5308666,19.01442 C23.6910226,19.459733 23.5152416,19.955826 23.1128976,20.20192 L23.1128976,20.20192 L20.2066476,22.38942 L25.7989286,22.3893123 L25.7989286,24.3893123 L20.2066476,24.38942 L23.1128976,26.57692 C23.5621166,26.912858 23.6519606,27.549576 23.3160226,27.998795 C22.9800856,28.448014 22.3433666,28.537858 21.8941476,28.20192 L21.8941476,28.20192 L16.6128976,24.20192 C16.3511786,24.01442 16.1949286,23.709733 16.1949286,23.38942 C16.1949286,23.069108 16.3511786,22.76442 16.6128976,22.57692 L16.6128976,22.57692 L21.8941476,18.57692 C22.0230536,18.479264 22.1714916,18.416764 22.3316476,18.38942 C22.3707106,18.377701 22.4136786,18.365983 22.4566476,18.35817 Z" id="arrow_right" transform="translate(20.997393, 23.377149) rotate(-90.000000) translate(-20.997393, -23.377149) "></path>
392
+ </g>
393
+ </g>
394
+ </g>
378
395
  </svg>""",
379
396
  "col_schema_match": """<?xml version="1.0" encoding="UTF-8"?>
380
397
  <svg width="67px" height="67px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
@@ -440,6 +457,18 @@ SVG_ICONS_FOR_ASSERTION_TYPES = {
440
457
  <path d="M51.8485976,12 L15.5758703,12 C13.9986329,12 12.712234,13.2863989 12.712234,14.8636364 L12.712234,51.1363636 C12.712234,52.7136011 13.9986329,54 15.5758703,54 L51.8485976,54 C53.4258351,54 54.712234,52.7136011 54.712234,51.1363636 L54.712234,14.8636364 C54.712234,13.2863989 53.4258351,12 51.8485976,12 Z M37.072234,44 L20.272234,44 L20.272234,42 L37.072234,42 L37.072234,44 Z M37.072234,34 L20.272234,34 L20.272234,32 L37.072234,32 L37.072234,34 Z M37.072234,24 L20.272234,24 L20.272234,22 L37.072234,22 L37.072234,24 Z M47.9233279,41.773438 L45.5706719,45.773438 C45.4427029,45.996094 45.239265,46.148438 45.0095779,46.1875 C44.9702029,46.195313 44.9275469,46.199219 44.88489,46.199219 C44.70114,46.199219 44.5206719,46.128906 44.373015,45.992188 L42.1877029,43.992188 C41.8202029,43.65625 41.7512969,43.027344 42.033484,42.589844 C42.3156719,42.152344 42.8439529,42.070313 43.2114529,42.40625 L44.697859,43.769531 L46.548484,40.625 C46.814265,40.171875 47.335984,40.0625 47.716609,40.378906 C48.097234,40.695313 48.189109,41.320313 47.9233279,41.773438 Z M47.9233279,31.773438 L45.5706719,35.773438 C45.4427029,35.996094 45.239265,36.148438 45.0095779,36.1875 C44.9702029,36.195313 44.9275469,36.199219 44.88489,36.199219 C44.70114,36.199219 44.5206719,36.128906 44.373015,35.992188 L42.1877029,33.992188 C41.8202029,33.65625 41.7512969,33.027344 42.033484,32.589844 C42.3156719,32.152344 42.8439529,32.070313 43.2114529,32.40625 L44.697859,33.769531 L46.548484,30.628906 C46.814265,30.175781 47.335984,30.0625 47.716609,30.382813 C48.097234,30.699219 48.189109,31.320313 47.9233279,31.773438 Z M47.9233279,21.773438 L45.5706719,25.773438 C45.4427029,25.996094 45.239265,26.148438 45.0095779,26.1875 C44.9702029,26.195313 44.9275469,26.199219 44.88489,26.199219 C44.70114,26.199219 44.5206719,26.128906 44.373015,25.992188 L42.1877029,23.992188 C41.8202029,23.65625 41.7512969,23.027344 42.033484,22.589844 C42.3156719,22.152344 42.8439529,22.070313 43.2114529,22.40625 L44.697859,23.769531 L46.548484,20.625 C46.814265,20.171875 47.335984,20.0625 47.716609,20.378906 C48.097234,20.699219 48.189109,21.320313 47.9233279,21.773438 Z" id="conjoint" fill="#000000" fill-rule="nonzero"></path>
441
458
  </g>
442
459
  </g>
460
+ </svg>""",
461
+ "specially": """<?xml version="1.0" encoding="UTF-8"?>
462
+ <svg width="67px" height="67px" viewBox="0 0 67 67" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
463
+ <title>specially</title>
464
+ <g id="All-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
465
+ <g id="specially" transform="translate(0.000000, 0.206897)">
466
+ <path d="M56.712234,1 C59.1975153,1 61.4475153,2.00735931 63.076195,3.63603897 C64.7048747,5.26471863 65.712234,7.51471863 65.712234,10 L65.712234,10 L65.712234,65 L10.712234,65 C8.22695259,65 5.97695259,63.9926407 4.34827294,62.363961 C2.71959328,60.7352814 1.71223397,58.4852814 1.71223397,56 L1.71223397,56 L1.71223397,10 C1.71223397,7.51471863 2.71959328,5.26471863 4.34827294,3.63603897 C5.97695259,2.00735931 8.22695259,1 10.712234,1 L10.712234,1 Z" id="rectangle" stroke="#000000" stroke-width="2" fill="#FFFFFF"></path>
467
+ <g id="star" transform="translate(8.500000, 8.500000)" fill="#000000" fill-rule="nonzero">
468
+ <path d="M25,0 C24.5874484,0 24.2174517,0.254002847 24.068359,0.6386719 L17.902344,16.535156 L0.94921875,17.400391 C0.536124409,17.4213013 0.17852458,17.6943523 0.0495446395,18.0873515 C-0.0794353012,18.4803507 0.046820452,18.9122002 0.3671875,19.173828 L13.568359,29.966797 L9.2324219,46.34375 C9.12646963,46.7428009 9.27663058,47.1659433 9.61042698,47.4089402 C9.94422338,47.651937 10.3930345,47.664834 10.740234,47.441406 L25,38.289062 L39.259766,47.441406 C39.6069655,47.6648339 40.0557766,47.6519369 40.3895729,47.4089401 C40.7233693,47.1659432 40.8735302,46.7428009 40.767578,46.34375 L36.431641,29.966797 L49.632812,19.173828 C49.953179,18.9122002 50.0794348,18.4803507 49.9504549,18.0873516 C49.821475,17.6943524 49.4638753,17.4213014 49.050781,17.400391 L32.097656,16.535156 L25.931641,0.6386719 C25.7825483,0.254002847 25.4125516,0 25,0 Z M25,3.7636719 L30.466797,17.861328 C30.609689,18.2291416 30.9554962,18.4785515 31.349609,18.498047 L46.359375,19.265625 L34.667969,28.826172 C34.3646054,29.0742114 34.2340493,29.4765679 34.333984,29.855469 L38.175781,44.369141 L25.541016,36.257812 C25.2114789,36.0458536 24.7885211,36.0458536 24.458984,36.257812 L11.824219,44.369141 L15.666016,29.855469 C15.7659507,29.4765679 15.6353946,29.0742114 15.332031,28.826172 L3.640625,19.265625 L18.650391,18.498047 C19.0445038,18.4785515 19.390311,18.2291416 19.533203,17.861328 L25,3.7636719 Z" id="Shape"></path>
469
+ </g>
470
+ </g>
471
+ </g>
443
472
  </svg>""",
444
473
  }
445
474
 
@@ -728,6 +728,114 @@ EXPECT_FAIL_TEXT = {
728
728
  "hi": "चयनित स्तंभों में पंक्तियां पूरी तरह से अलग नहीं थीं, ऐसे असफल परीक्षण इकाइयों की अधिकता।",
729
729
  "el": "Υπέρβαση αποτυχημένων μονάδων δοκιμής όπου δεν υπήρχαν διακριτές γραμμές στις επιλεγμένες στήλες.",
730
730
  },
731
+ "all_row_complete_expectation_text": {
732
+ "en": "Expect entirely complete rows across all columns.",
733
+ "fr": "On s'attend à des lignes entièrement complètes dans toutes les colonnes.",
734
+ "de": "Erwarten Sie vollständig komplette Zeilen über alle Spalten hinweg.",
735
+ "it": "Aspettati righe completamente complete su tutte le colonne.",
736
+ "es": "Se espera que las filas estén completamente completas en todas las columnas.",
737
+ "pt": "Espera-se linhas completamente preenchidas em todas as colunas.",
738
+ "ro": "Se așteaptă ca rândurile să fie complet complete în toate coloanele.",
739
+ "tr": "Tüm sütunlarda tamamen eksiksiz satırlar bekleyin.",
740
+ "zh-Hans": "预期所有列中的行都是完整的。",
741
+ "zh-Hant": "預期所有列中的行都是完整的。",
742
+ "ja": "すべての列で完全に完全な行を期待します。",
743
+ "ko": "모든 열에서 완전히 완성된 행을 기대합니다.",
744
+ "vi": "Kỳ vọng các hàng hoàn toàn đầy đủ trên tất cả các cột.",
745
+ "ru": "Ожидайте полностью заполненные строки по всем столбцам.",
746
+ "cs": "Očekávejte zcela kompletní řádky ve všech sloupcích.",
747
+ "pl": "Spodziewaj się w pełni kompletnych wierszy we wszystkich kolumnach.",
748
+ "da": "Forvent helt komplette rækker på tværs af alle kolonner.",
749
+ "sv": "Förvänta dig helt kompletta rader över alla kolumner.",
750
+ "nb": "Forvent helt komplette rader på tvers av alle kolonner.",
751
+ "nl": "Verwacht volledig complete rijen in alle kolommen.",
752
+ "fi": "Odota täysin täydellisiä rivejä kaikissa sarakkeissa.",
753
+ "is": "Væntir þess að allar raðir séu heildstæðar yfir alla dálka.",
754
+ "ar": "توقع صفوف مكتملة تمامًا عبر جميع الأعمدة.",
755
+ "hi": "सभी स्तंभों में पूरी तरह से पूर्ण पंक्तियों की अपेक्षा करें।",
756
+ "el": "Αναμένεται πλήρως ολοκληρωμένες γραμμές σε όλες τις στήλες.",
757
+ },
758
+ "all_row_complete_failure_text": {
759
+ "en": "Exceedance of failed test units where there weren't complete rows across all columns.",
760
+ "fr": "Dépassement des unités de test ayant échoué là où il n'y avait pas de lignes complètes dans toutes les colonnes.",
761
+ "de": "Überschreitung fehlgeschlagener Testeinheiten, bei denen nicht vollständige Zeilen über alle Spalten hinweg vorhanden waren.",
762
+ "it": "Superamento delle unità di test fallite in cui non c'erano righe complete su tutte le colonne.",
763
+ "es": "Se superó el número de unidades de prueba fallidas donde no había filas completas en todas las columnas.",
764
+ "pt": "Excedeu o número de unidades de teste com falha onde não havia linhas completas em todas as colunas.",
765
+ "ro": "Depășirea unităților de test eșuate unde nu au existat rânduri complete în toate coloanele.",
766
+ "tr": "Tüm sütunlarda eksiksiz satırların olmadığı başarısız test birimlerinin aşılması.",
767
+ "zh-Hans": "错误过多,其中在所有列中行不完整。",
768
+ "zh-Hant": "錯誤過多,在所有列中沒有完整的行。",
769
+ "ja": "すべての列で完全な行がないテスト単位の失敗の超過。",
770
+ "ko": "모든 열에 걸쳐 완전한 행이 아니었던 실패한 테스트 단위 초과.",
771
+ "vi": "Vượt quá số đơn vị kiểm tra thất bại trong đó không có các hàng đầy đủ trên tất cả các cột.",
772
+ "ru": "Превышение неудачных тестовых единиц, где не было полных строк по всем столбцам.",
773
+ "cs": "Překročení počtu neúspěšných testů, kde nebyly úplné řádky ve všech sloupcích.",
774
+ "pl": "Przekroczenie liczby niepomyślnych jednostek testowych, w których nie było kompletnych wierszy we wszystkich kolumnach.",
775
+ "da": "Overskridelse af antal fejlslagne enhedstests, hvor der ikke var komplette rækker på tværs af alle kolonner.",
776
+ "sv": "Överstiger antalet misslyckade enhetstest där det inte fanns kompletta rader över alla kolumner.",
777
+ "nb": "Overskridelse av mislykkede testenheter hvor det ikke var komplette rader på tvers av alle kolonner.",
778
+ "nl": "Overschrijding van mislukte testeenheden waarbij er geen complete rijen waren in alle kolommen.",
779
+ "fi": "Epäonnistuneiden testiyksikköjen ylitys, joissa ei ollut täydellisiä rivejä kaikissa sarakkeissa.",
780
+ "is": "Of mörg misheppnuð próf þar sem raðir voru ekki heildstæðar yfir alla dálka.",
781
+ "ar": "تجاوز وحدات الاختبار الفاشلة حيث لم تكن هناك صفوف مكتملة عبر جميع الأعمدة.",
782
+ "hi": "सभी स्तंभों में पूर्ण पंक्तियां नहीं थीं, ऐसे असफल परीक्षण इकाइयों की अधिकता।",
783
+ "el": "Υπέρβαση αποτυχημένων μονάδων δοκιμής όπου δεν υπήρχαν πλήρεις γραμμές σε όλες τις στήλες.",
784
+ },
785
+ "across_row_complete_expectation_text": {
786
+ "en": "Expect entirely complete rows across {column_text}.",
787
+ "fr": "On s'attend à des lignes entièrement complètes dans {column_text}.",
788
+ "de": "Erwarten Sie vollständig komplette Zeilen über {column_text} hinweg.",
789
+ "it": "Aspettati righe completamente complete su {column_text}.",
790
+ "es": "Se espera que las filas estén completamente completas en {column_text}.",
791
+ "pt": "Espera-se linhas completamente preenchidas em {column_text}.",
792
+ "ro": "Se așteaptă ca rândurile să fie complet complete în {column_text}.",
793
+ "tr": "{column_text} boyunca tamamen eksiksiz satırlar bekleyin.",
794
+ "zh-Hans": "预期在{column_text}中的行是完整的。",
795
+ "zh-Hant": "預期在{column_text}中的行是完整的。",
796
+ "ja": "{column_text}において完全に完全な行を期待します。",
797
+ "ko": "{column_text}에서 완전히 완성된 행을 기대합니다.",
798
+ "vi": "Kỳ vọng các hàng hoàn toàn đầy đủ trên {column_text}.",
799
+ "ru": "Ожидайте полностью заполненные строки в {column_text}.",
800
+ "cs": "Očekávejte zcela kompletní řádky v {column_text}.",
801
+ "pl": "Spodziewaj się w pełni kompletnych wierszy w {column_text}.",
802
+ "da": "Forvent helt komplette rækker på tværs af {column_text}.",
803
+ "sv": "Förvänta dig helt kompletta rader över {column_text}.",
804
+ "nb": "Forvent helt komplette rader på tvers av {column_text}.",
805
+ "nl": "Verwacht volledig complete rijen in {column_text}.",
806
+ "fi": "Odota täysin täydellisiä rivejä sarakkeissa {column_text}.",
807
+ "is": "Væntir þess að allar raðir séu heildstæðar yfir {column_text}.",
808
+ "ar": "توقع صفوف مكتملة تمامًا عبر {column_text}.",
809
+ "hi": "{column_text} में पूरी तरह से पूर्ण पंक्तियों की अपेक्षा करें।",
810
+ "el": "Αναμένεται πλήρως ολοκληρωμένες γραμμές στις στήλες {column_text}.",
811
+ },
812
+ "across_row_complete_failure_text": {
813
+ "en": "Exceedance of failed test units where there weren't complete rows across selected columns.",
814
+ "fr": "Dépassement des unités de test ayant échoué là où il n'y avait pas de lignes complètes dans les colonnes sélectionnées.",
815
+ "de": "Überschreitung fehlgeschlagener Testeinheiten, bei denen nicht vollständige Zeilen über die ausgewählten Spalten hinweg vorhanden waren.",
816
+ "it": "Superamento delle unità di test fallite in cui non c'erano righe complete nelle colonne selezionate.",
817
+ "es": "Se superó el número de unidades de prueba fallidas donde no había filas completas en las columnas seleccionadas.",
818
+ "pt": "Excedeu o número de unidades de teste com falha onde não havia linhas completas nas colunas selecionadas.",
819
+ "ro": "Depășirea unităților de test eșuate unde nu au existat rânduri complete în coloanele selectate.",
820
+ "tr": "Seçili sütunlarda eksiksiz satırların olmadığı başarısız test birimlerinin aşılması.",
821
+ "zh-Hans": "错误过多,其中在所选列中行不完整。",
822
+ "zh-Hant": "錯誤過多,在所選列中沒有完整的行。",
823
+ "ja": "選択された列で完全な行がないテスト単位の失敗の超過。",
824
+ "ko": "선택된 열에서 완전한 행이 아니었던 실패한 테스트 단위 초과.",
825
+ "vi": "Vượt quá số đơn vị kiểm tra thất bại trong đó không có các hàng đầy đủ trên các cột đã chọn.",
826
+ "ru": "Превышение неудачных тестовых единиц, где не было полных строк в выбранных столбцах.",
827
+ "cs": "Překročení počtu neúspěšných testů, kde nebyly úplné řádky ve vybraných sloupcích.",
828
+ "pl": "Przekroczenie liczby niepomyślnych jednostek testowych, w których nie było kompletnych wierszy w wybranych kolumnach.",
829
+ "da": "Overskridelse af antal fejlslagne enhedstests, hvor der ikke var komplette rækker på tværs af valgte kolonner.",
830
+ "sv": "Överstiger antalet misslyckade enhetstest där det inte fanns kompletta rader över valda kolumner.",
831
+ "nb": "Overskridelse av mislykkede testenheter hvor det ikke var komplette rader på tvers av valgte kolonner.",
832
+ "nl": "Overschrijding van mislukte testeenheden waarbij er geen complete rijen waren in geselecteerde kolommen.",
833
+ "fi": "Epäonnistuneiden testiyksikköjen ylitys, joissa ei ollut täydellisiä rivejä valituissa sarakkeissa.",
834
+ "is": "Of mörg misheppnuð próf þar sem raðir voru ekki heildstæðar yfir valda dálka.",
835
+ "ar": "تجاوز وحدات الاختبار الفاشلة حيث لم تكن هناك صفوف مكتملة عبر الأعمدة المحددة.",
836
+ "hi": "चयनित स्तंभों में पूर्ण पंक्तियां नहीं थीं, ऐसे असफल परीक्षण इकाइयों की अधिकता।",
837
+ "el": "Υπέρβαση αποτυχημένων μονάδων δοκιμής όπου δεν υπήρχαν πλήρεις γραμμές στις επιλεγμένες στήλες.",
838
+ },
731
839
  "col_schema_match_expectation_text": {
732
840
  "en": "Expect that column schemas match.",
733
841
  "fr": "On s'attend à ce que les schémas de colonnes correspondent.",
@@ -1052,6 +1160,60 @@ EXPECT_FAIL_TEXT = {
1052
1160
  "hi": "असफल परीक्षण इकाइयों की अधिकता जहां संयुक्त 'पास' इकाइयां होनी चाहिए थीं।",
1053
1161
  "el": "Υπέρβαση αποτυχημένων μονάδων δοκιμής όπου θα έπρεπε να υπάρχουν κοινές μονάδες 'επιτυχίας'.",
1054
1162
  },
1163
+ "specially_expectation_text": {
1164
+ "en": "Expect that special testing with a given function yields agreement.",
1165
+ "fr": "On s'attend à ce que les tests spéciaux avec une fonction donnée produisent un accord.",
1166
+ "de": "Erwarten Sie, dass spezielle Tests mit einer bestimmten Funktion Übereinstimmung ergeben.",
1167
+ "it": "Aspettati che i test speciali con una funzione data producano accordo.",
1168
+ "es": "Se espera que las pruebas especiales con una función dada produzcan concordancia.",
1169
+ "pt": "Espera-se que testes especiais com uma função dada produzam concordância.",
1170
+ "ro": "Se așteaptă ca testarea specială cu o funcție dată să producă acord.",
1171
+ "tr": "Belirli bir fonksiyonla özel testlerin uyum sağlamasını bekleyin.",
1172
+ "zh-Hans": "预期使用给定函数的特殊测试会产生一致结果。",
1173
+ "zh-Hant": "預期使用給定函數的特殊測試會產生一致結果。",
1174
+ "ja": "指定された関数による特別なテストが一致することを期待します。",
1175
+ "ko": "주어진 함수로 특수 테스트를 수행하면 일치함을 기대합니다.",
1176
+ "vi": "Kỳ vọng rằng kiểm tra đặc biệt với một hàm đã cho sẽ cho kết quả phù hợp.",
1177
+ "ru": "Ожидайте, что специальное тестирование с заданной функцией дает согласие.",
1178
+ "cs": "Očekává se, že speciální testování s danou funkcí přinese shodu.",
1179
+ "pl": "Oczekuj, że specjalne testowanie z użyciem danej funkcji przyniesie zgodność.",
1180
+ "da": "Forvent at speciel test med en given funktion giver overensstemmelse.",
1181
+ "sv": "Förvänta dig att speciell testning med en given funktion ger överensstämmelse.",
1182
+ "nb": "Forvent at spesiell testing med en gitt funksjon gir samsvar.",
1183
+ "nl": "Verwacht dat speciale tests met een gegeven functie overeenstemming opleveren.",
1184
+ "fi": "Odota, että erityinen testaus annetulla funktiolla tuottaa yhdenmukaisuuden.",
1185
+ "is": "Væntir þess að sérstök prófun með gefnu falli leiði til samræmis.",
1186
+ "ar": "توقع أن الاختبار الخاص بدالة معينة يؤدي إلى التوافق.",
1187
+ "hi": "अपेक्षा है कि दिए गए फ़ंक्शन के साथ विशेष परीक्षण सहमति प्रदान करेगा।",
1188
+ "el": "Αναμένεται ότι ο ειδικός έλεγχος με μια δεδομένη συνάρτηση αποδίδει συμφωνία.",
1189
+ },
1190
+ "specially_failure_text": {
1191
+ "en": "Exceedance of failed test units when performing specialized testing with a given function.",
1192
+ "fr": "Dépassement des unités de test ayant échoué lors de l'exécution de tests spécialisés avec une fonction donnée.",
1193
+ "de": "Überschreitung fehlgeschlagener Testeinheiten bei der Durchführung spezialisierter Tests mit einer bestimmten Funktion.",
1194
+ "it": "Superamento delle unità di test fallite durante l'esecuzione di test specializzati con una funzione data.",
1195
+ "es": "Se superó el número de unidades de prueba fallidas al realizar pruebas especializadas con una función dada.",
1196
+ "pt": "Excedeu o número de unidades de teste com falha ao realizar testes especializados com uma função dada.",
1197
+ "ro": "Depășirea unităților de test eșuate la efectuarea testării specializate cu o funcție dată.",
1198
+ "tr": "Belirli bir fonksiyonla özel testler yapılırken başarısız test birimlerinin aşılması.",
1199
+ "zh-Hans": "使用给定函数进行专门测试时,失败的测试单元数量超标。",
1200
+ "zh-Hant": "使用給定函數進行專門測試時,失敗的測試單元數量超標。",
1201
+ "ja": "指定された関数を使用した特殊テスト実行時のテスト単位の失敗の超過。",
1202
+ "ko": "주어진 함수로 특수 테스트를 수행할 때 실패한 테스트 단위 초과.",
1203
+ "vi": "Vượt quá số đơn vị kiểm tra thất bại khi thực hiện kiểm tra chuyên biệt với một hàm đã cho.",
1204
+ "ru": "Превышение неудачных тестовых единиц при выполнении специализированного тестирования с заданной функцией.",
1205
+ "cs": "Překročení počtu neúspěšných testovacích jednotek při provádění specializovaného testování s danou funkcí.",
1206
+ "pl": "Przekroczenie nieudanych jednostek testowych podczas przeprowadzania specjalistycznych testów z daną funkcją.",
1207
+ "da": "Overskridelse af fejlslagne testenheder ved udførelse af specialiseret test med en given funktion.",
1208
+ "sv": "Överskrider antalet misslyckade testenheter vid utförande av specialiserad testning med en given funktion.",
1209
+ "nb": "Overskridelse av mislykkede testenheter ved utførelse av spesialisert testing med en gitt funksjon.",
1210
+ "nl": "Overschrijding van mislukte testeenheden bij het uitvoeren van gespecialiseerde tests met een gegeven functie.",
1211
+ "fi": "Epäonnistuneiden testiyksiköiden ylitys suoritettaessa erikoistestejä annetulla funktiolla.",
1212
+ "is": "Of mörg misheppnuð próf við framkvæmd sérhæfðra prófana með gefnu falli.",
1213
+ "ar": "تجاوز وحدات الاختبار الفاشلة عند إجراء اختبار متخصص بدالة معينة.",
1214
+ "hi": "दिए गए फ़ंक्शन के साथ विशेष परीक्षण करते समय असफल परीक्षण इकाइयों की अधिकता।",
1215
+ "el": "Υπέρβαση αποτυχημένων μονάδων δοκιμής κατά την εκτέλεση εξειδικευμένων ελέγχων με μια δεδομένη συνάρτηση.",
1216
+ },
1055
1217
  }
1056
1218
 
1057
1219
 
@@ -1735,6 +1897,60 @@ STEP_REPORT_TEXT = {
1735
1897
  "hi": "पंक्तियां स्तंभों के एक उपसमूह में अलग-अलग हैं",
1736
1898
  "el": "Οι γραμμές είναι διακριτές σε ένα υποσύνολο στηλών",
1737
1899
  },
1900
+ "rows_complete_all": {
1901
+ "en": "All rows are complete",
1902
+ "fr": "Toutes les lignes sont complètes",
1903
+ "de": "Alle Zeilen sind vollständig",
1904
+ "it": "Tutte le righe sono complete",
1905
+ "es": "Todas las filas están completas",
1906
+ "pt": "Todas as linhas estão completas",
1907
+ "ro": "Toate rândurile sunt complete",
1908
+ "tr": "Tüm satırlar eksiksizdir",
1909
+ "zh-Hans": "所有行都是完整的",
1910
+ "zh-Hant": "所有行都是完整的",
1911
+ "ja": "すべての行が完全です",
1912
+ "ko": "모든 행이 완전합니다",
1913
+ "vi": "Tất cả các hàng đều đầy đủ",
1914
+ "ru": "Все строки заполнены полностью",
1915
+ "cs": "Všechny řádky jsou úplné",
1916
+ "pl": "Wszystkie wiersze są kompletne",
1917
+ "da": "Alle rækker er komplette",
1918
+ "sv": "Alla rader är kompletta",
1919
+ "nb": "Alle rader er komplette",
1920
+ "nl": "Alle rijen zijn compleet",
1921
+ "fi": "Kaikki rivit ovat täydellisiä",
1922
+ "is": "Allar raðir eru heildstæðar",
1923
+ "ar": "جميع الصفوف مكتملة",
1924
+ "hi": "सभी पंक्तियां पूर्ण हैं",
1925
+ "el": "Όλες οι γραμμές είναι πλήρεις",
1926
+ },
1927
+ "rows_complete_subset": {
1928
+ "en": "Rows are complete across a subset of columns",
1929
+ "fr": "Les lignes sont complètes sur un sous-ensemble de colonnes",
1930
+ "de": "Zeilen sind in einer Teilmenge von Spalten vollständig",
1931
+ "it": "Le righe sono complete in un sottoinsieme di colonne",
1932
+ "es": "Las filas están completas en un subconjunto de columnas",
1933
+ "pt": "As linhas estão completas em um subconjunto de colunas",
1934
+ "ro": "Rândurile sunt complete într-un subset de coloane",
1935
+ "tr": "Satırlar, sütunların bir alt kümesinde eksiksizdir",
1936
+ "zh-Hans": "行在列的子集中是完整的",
1937
+ "zh-Hant": "行在列的子集中是完整的",
1938
+ "ja": "行は列のサブセット間で完全です",
1939
+ "ko": "행이 열의 하위 집합에서 완전합니다",
1940
+ "vi": "Các hàng đầy đủ trong một tập con của các cột",
1941
+ "ru": "Строки полностью заполнены в подмножестве столбцов",
1942
+ "cs": "Řádky jsou úplné napříč podmnožinou sloupců",
1943
+ "pl": "Wiersze są kompletne w podzbiorze kolumn",
1944
+ "da": "Rækker er komplette på tværs af en delmængde af kolonner",
1945
+ "sv": "Rader är kompletta över en delmängd av kolumner",
1946
+ "nb": "Rader er komplette på tvers av en delmengde av kolonner",
1947
+ "nl": "Rijen zijn compleet over een subset van kolommen",
1948
+ "fi": "Rivit ovat täydellisiä sarakkeiden osajoukossa",
1949
+ "is": "Raðir eru heildstæðar í undirsafni dálka",
1950
+ "ar": "الصفوف مكتملة عبر مجموعة فرعية من الأعمدة",
1951
+ "hi": "पंक्तियां स्तंभों के एक उपसमूह में पूर्ण हैं",
1952
+ "el": "Οι γραμμές είναι πλήρεις σε ένα υποσύνολο στηλών",
1953
+ },
1738
1954
  "report_for_step_i": {
1739
1955
  "en": "Report for Validation Step {i}",
1740
1956
  "fr": "Rapport pour l'étape de validation {i}",
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import functools
3
4
  from dataclasses import dataclass
4
5
  from typing import TYPE_CHECKING, Any
5
6
 
@@ -1219,6 +1220,36 @@ class Interrogator:
1219
1220
 
1220
1221
  return tbl.to_native()
1221
1222
 
1223
+ def rows_complete(self) -> FrameT | Any:
1224
+ # Ibis backends ---------------------------------------------
1225
+
1226
+ if self.tbl_type in IBIS_BACKENDS:
1227
+ tbl = self.x
1228
+
1229
+ # Determine the number of null values in each row (column subsets are handled in
1230
+ # the `_check_nulls_across_columns_ibis()` function)
1231
+ tbl = _check_nulls_across_columns_ibis(table=tbl, columns_subset=self.columns_subset)
1232
+
1233
+ # Failing rows will have the value `True` in the generated column, so we need to negate
1234
+ # the result to get the passing rows
1235
+ return tbl.mutate(pb_is_good_=~tbl["_any_is_null_"]).drop("_any_is_null_")
1236
+
1237
+ # Local backends (Narwhals) ---------------------------------
1238
+
1239
+ tbl = self.x
1240
+
1241
+ # Determine the number of null values in each row (column subsets are handled in
1242
+ # the `_check_nulls_across_columns_nw()` function)
1243
+ tbl = _check_nulls_across_columns_nw(table=tbl, columns_subset=self.columns_subset)
1244
+
1245
+ # Failing rows will have the value `True` in the generated column, so we need to negate
1246
+ # the result to get the passing rows
1247
+ tbl = tbl.with_columns(pb_is_good_=~nw.col("_any_is_null_"))
1248
+ tbl = tbl.drop("_any_is_null_")
1249
+
1250
+ # Convert the table to a native format
1251
+ return tbl.to_native()
1252
+
1222
1253
 
1223
1254
  @dataclass
1224
1255
  class ColValsCompareOne:
@@ -1794,6 +1825,58 @@ class RowsDistinct:
1794
1825
  return self.test_unit_res
1795
1826
 
1796
1827
 
1828
+ @dataclass
1829
+ class RowsComplete:
1830
+ """
1831
+ Check if rows in a DataFrame are complete.
1832
+
1833
+ Parameters
1834
+ ----------
1835
+ data_tbl
1836
+ A data table.
1837
+ columns_subset
1838
+ A list of columns to check for completeness.
1839
+ threshold
1840
+ The maximum number of failing test units to allow.
1841
+ tbl_type
1842
+ The type of table to use for the assertion.
1843
+
1844
+ Returns
1845
+ -------
1846
+ bool
1847
+ `True` when test units pass below the threshold level for failing test units, `False`
1848
+ otherwise.
1849
+ """
1850
+
1851
+ data_tbl: FrameT
1852
+ columns_subset: list[str] | None
1853
+ threshold: int
1854
+ tbl_type: str = "local"
1855
+
1856
+ def __post_init__(self):
1857
+ if self.tbl_type == "local":
1858
+ # Convert the DataFrame to a format that narwhals can work with, and:
1859
+ # - check if the `column=` exists
1860
+ # - check if the `column=` type is compatible with the test
1861
+ tbl = _column_subset_test_prep(df=self.data_tbl, columns_subset=self.columns_subset)
1862
+
1863
+ # TODO: For Ibis backends, check if the column exists and if the column type is compatible;
1864
+ # for now, just pass the table as is
1865
+ if self.tbl_type in IBIS_BACKENDS:
1866
+ tbl = self.data_tbl
1867
+
1868
+ # Collect results for the test units; the results are a list of booleans where
1869
+ # `True` indicates a passing test unit
1870
+ self.test_unit_res = Interrogator(
1871
+ x=tbl,
1872
+ columns_subset=self.columns_subset,
1873
+ tbl_type=self.tbl_type,
1874
+ ).rows_complete()
1875
+
1876
+ def get_test_results(self):
1877
+ return self.test_unit_res
1878
+
1879
+
1797
1880
  @dataclass
1798
1881
  class ColSchemaMatch:
1799
1882
  """
@@ -2165,6 +2248,107 @@ class ConjointlyValidation:
2165
2248
  return results_tbl
2166
2249
 
2167
2250
 
2251
+ class SpeciallyValidation:
2252
+ def __init__(self, data_tbl, expression, threshold, tbl_type):
2253
+ self.data_tbl = data_tbl
2254
+ self.expression = expression
2255
+ self.threshold = threshold
2256
+
2257
+ # Detect the table type
2258
+ if tbl_type in (None, "local"):
2259
+ # Detect the table type using _get_tbl_type()
2260
+ self.tbl_type = _get_tbl_type(data=data_tbl)
2261
+ else:
2262
+ self.tbl_type = tbl_type
2263
+
2264
+ def get_test_results(self) -> any | list[bool]:
2265
+ """Evaluate the expression get either a list of booleans or a results table."""
2266
+
2267
+ # Get the expression and inspect whether there is a `data` argument
2268
+ expression = self.expression
2269
+
2270
+ import inspect
2271
+
2272
+ # During execution of `specially` validation
2273
+ sig = inspect.signature(expression)
2274
+ params = list(sig.parameters.keys())
2275
+
2276
+ # Execute the function based on its signature
2277
+ if len(params) == 0:
2278
+ # No parameters: call without arguments
2279
+ result = expression()
2280
+ elif len(params) == 1:
2281
+ # One parameter: pass the data table
2282
+ data_tbl = self.data_tbl
2283
+ result = expression(data_tbl)
2284
+ else:
2285
+ # More than one parameter - this doesn't match either allowed signature
2286
+ raise ValueError(
2287
+ f"The function provided to 'specially()' should have either no parameters or a "
2288
+ f"single 'data' parameter, but it has {len(params)} parameters: {params}"
2289
+ )
2290
+
2291
+ # Determine if the object is a DataFrame by inspecting the string version of its type
2292
+ if (
2293
+ "pandas" in str(type(result))
2294
+ or "polars" in str(type(result))
2295
+ or "ibis" in str(type(result))
2296
+ ):
2297
+ # Get the type of the table
2298
+ tbl_type = _get_tbl_type(data=result)
2299
+
2300
+ if "pandas" in tbl_type:
2301
+ # If it's a Pandas DataFrame, check if the last column is a boolean column
2302
+ last_col = result.iloc[:, -1]
2303
+
2304
+ import pandas as pd
2305
+
2306
+ if last_col.dtype == bool or pd.api.types.is_bool_dtype(last_col):
2307
+ # If the last column is a boolean column, rename it as `pb_is_good_`
2308
+ result.rename(columns={result.columns[-1]: "pb_is_good_"}, inplace=True)
2309
+ elif "polars" in tbl_type:
2310
+ # If it's a Polars DataFrame, check if the last column is a boolean column
2311
+ last_col_name = result.columns[-1]
2312
+ last_col_dtype = result.schema[last_col_name]
2313
+
2314
+ import polars as pl
2315
+
2316
+ if last_col_dtype == pl.Boolean:
2317
+ # If the last column is a boolean column, rename it as `pb_is_good_`
2318
+ result = result.rename({last_col_name: "pb_is_good_"})
2319
+ elif tbl_type in IBIS_BACKENDS:
2320
+ # If it's an Ibis table, check if the last column is a boolean column
2321
+ last_col_name = result.columns[-1]
2322
+ result_schema = result.schema()
2323
+ is_last_col_bool = str(result_schema[last_col_name]) == "boolean"
2324
+
2325
+ if is_last_col_bool:
2326
+ # If the last column is a boolean column, rename it as `pb_is_good_`
2327
+ result = result.rename(pb_is_good_=last_col_name)
2328
+
2329
+ else: # pragma: no cover
2330
+ raise NotImplementedError(f"Support for {tbl_type} is not yet implemented")
2331
+
2332
+ elif isinstance(result, bool):
2333
+ # If it's a single boolean, return that as a list
2334
+ return [result]
2335
+
2336
+ elif isinstance(result, list):
2337
+ # If it's a list, check that it is a boolean list
2338
+ if all(isinstance(x, bool) for x in result):
2339
+ # If it's a list of booleans, return it as is
2340
+ return result
2341
+ else:
2342
+ # If it's not a list of booleans, raise an error
2343
+ raise TypeError("The result is not a list of booleans.")
2344
+ else: # pragma: no cover
2345
+ # If it's not a DataFrame or a list, raise an error
2346
+ raise TypeError("The result is not a DataFrame or a list of booleans.")
2347
+
2348
+ # Return the results table or list of booleans
2349
+ return result
2350
+
2351
+
2168
2352
  @dataclass
2169
2353
  class NumberOfTestUnits:
2170
2354
  """
@@ -2207,6 +2391,40 @@ def _column_has_null_values(table: FrameT, column: str) -> bool:
2207
2391
  return True
2208
2392
 
2209
2393
 
2394
+ def _check_nulls_across_columns_ibis(table, columns_subset):
2395
+ # Get all column names from the table
2396
+ column_names = columns_subset if columns_subset else table.columns
2397
+
2398
+ # Build the expression by combining each column's isnull() with OR operations
2399
+ null_expr = functools.reduce(
2400
+ lambda acc, col: acc | table[col].isnull() if acc is not None else table[col].isnull(),
2401
+ column_names,
2402
+ None,
2403
+ )
2404
+
2405
+ # Add the expression as a new column to the table
2406
+ result = table.mutate(_any_is_null_=null_expr)
2407
+
2408
+ return result
2409
+
2410
+
2411
+ def _check_nulls_across_columns_nw(table, columns_subset):
2412
+ # Get all column names from the table
2413
+ column_names = columns_subset if columns_subset else table.columns
2414
+
2415
+ # Build the expression by combining each column's `is_null()` with OR operations
2416
+ null_expr = functools.reduce(
2417
+ lambda acc, col: acc | table[col].is_null() if acc is not None else table[col].is_null(),
2418
+ column_names,
2419
+ None,
2420
+ )
2421
+
2422
+ # Add the expression as a new column to the table
2423
+ result = table.with_columns(_any_is_null_=null_expr)
2424
+
2425
+ return result
2426
+
2427
+
2210
2428
  def _modify_datetime_compare_val(tgt_column: any, compare_val: any) -> any:
2211
2429
  tgt_col_dtype_str = str(tgt_column.dtype).lower()
2212
2430
 
pointblank/_utils.py CHANGED
@@ -485,10 +485,12 @@ def _get_api_text() -> str:
485
485
  "Validate.col_vals_expr",
486
486
  "Validate.col_exists",
487
487
  "Validate.rows_distinct",
488
+ "Validate.rows_complete",
488
489
  "Validate.col_schema_match",
489
490
  "Validate.row_count_match",
490
491
  "Validate.col_count_match",
491
492
  "Validate.conjointly",
493
+ "Validate.specially",
492
494
  ]
493
495
 
494
496
  column_selection_exported = [
pointblank/actions.py CHANGED
@@ -225,7 +225,7 @@ def send_slack_notification(
225
225
  validation
226
226
  ```
227
227
 
228
- By placing the `notify_slack` function in the `Validate(actions=Actions(critical=))` argument,
228
+ By placing the `notify_slack()` function in the `Validate(actions=Actions(critical=))` argument,
229
229
  you can ensure that the notification is sent whenever the 'critical' threshold is reached (as
230
230
  set here, when 15% or more of the test units fail). The notification will include information
231
231
  about the validation step that triggered the alert.
@@ -255,7 +255,7 @@ def send_slack_notification(
255
255
  )
256
256
  ```
257
257
 
258
- In this case, the same `notify_slack` function is used, but it is placed in
258
+ In this case, the same `notify_slack()` function is used, but it is placed in
259
259
  `Validate(final_actions=FinalActions())`. This results in the summary notification being sent
260
260
  after all validation steps are completed, regardless of whether any steps failed or not.
261
261