apd
Advanced tools
+20
-25
| Metadata-Version: 2.1 | ||
| Name: apd | ||
| Version: 0.5.0 | ||
| Version: 0.6.0 | ||
| Summary: Tool to access the Analysis production Data | ||
@@ -19,3 +19,2 @@ License: BSD-3-Clause | ||
| Usage | ||
@@ -35,17 +34,11 @@ ===== | ||
| In [9]: datasets = apd.AnalysisData("SL", "RDs") | ||
| In [9]: datasets = apd.get_analysis_data("SL", "RDs") | ||
| In [10]: datasets(datatype="2012") | ||
| In [10]: datasets(datatype="2012", polarity="magdown") | ||
| Out[10]: | ||
| ['root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root', | ||
| ['root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000003_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000002_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000003_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000004_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000006_1.bsntuple_mc.root'] | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root'] | ||
@@ -60,13 +53,9 @@ In [11]: | ||
| $ apd-list-pfns SL RDs --datatype=2011 --datatype=2016 --polarity=magdown | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000001_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000002_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000003_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000001_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000002_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000003_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000004_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000005_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000006_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000007_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000003_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root' | ||
| The *apd-cache* command allows caching the Analysis metadata to a | ||
@@ -87,2 +76,8 @@ specific location. | ||
| BEWARE: The endpoint is experimental and can be interrupted at any time. | ||
| Further information | ||
| =================== | ||
| See: | ||
| https://lhcb-ap.docs.cern.ch/user_guide/accessing_output.html |
+19
-24
@@ -6,3 +6,2 @@ Analysis Production Data | ||
| Usage | ||
@@ -22,17 +21,11 @@ ===== | ||
| In [9]: datasets = apd.AnalysisData("SL", "RDs") | ||
| In [9]: datasets = apd.get_analysis_data("SL", "RDs") | ||
| In [10]: datasets(datatype="2012") | ||
| In [10]: datasets(datatype="2012", polarity="magdown") | ||
| Out[10]: | ||
| ['root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root', | ||
| ['root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000003_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000002_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000003_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000004_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000006_1.bsntuple_mc.root'] | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root'] | ||
@@ -47,13 +40,9 @@ In [11]: | ||
| $ apd-list-pfns SL RDs --datatype=2011 --datatype=2016 --polarity=magdown | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000001_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000002_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000003_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000001_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000002_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000003_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000004_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000005_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000006_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000007_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000003_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root' | ||
| The *apd-cache* command allows caching the Analysis metadata to a | ||
@@ -74,2 +63,8 @@ specific location. | ||
| BEWARE: The endpoint is experimental and can be interrupted at any time. | ||
| Further information | ||
| =================== | ||
| See: | ||
| https://lhcb-ap.docs.cern.ch/user_guide/accessing_output.html |
| Metadata-Version: 2.1 | ||
| Name: apd | ||
| Version: 0.5.0 | ||
| Version: 0.6.0 | ||
| Summary: Tool to access the Analysis production Data | ||
@@ -19,3 +19,2 @@ License: BSD-3-Clause | ||
| Usage | ||
@@ -35,17 +34,11 @@ ===== | ||
| In [9]: datasets = apd.AnalysisData("SL", "RDs") | ||
| In [9]: datasets = apd.get_analysis_data("SL", "RDs") | ||
| In [10]: datasets(datatype="2012") | ||
| In [10]: datasets(datatype="2012", polarity="magdown") | ||
| Out[10]: | ||
| ['root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root', | ||
| ['root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000003_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000002_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000003_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000004_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000005_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110972/0000/00110972_00000006_1.bsntuple_mc.root'] | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root', | ||
| 'root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root'] | ||
@@ -60,13 +53,9 @@ In [11]: | ||
| $ apd-list-pfns SL RDs --datatype=2011 --datatype=2016 --polarity=magdown | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000001_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000002_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2011/BSNTUPLE_MC.ROOT/00110968/0000/00110968_00000003_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000001_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000002_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000003_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000004_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000005_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000006_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2016/BSNTUPLE_MC.ROOT/00110984/0000/00110984_00000007_1.bsntuple_mc.root | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000002_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000005_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000003_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000001_1.bsntuple_mc.root' | ||
| root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/2012/BSNTUPLE_MC.ROOT/00110970/0000/00110970_00000004_1.bsntuple_mc.root' | ||
| The *apd-cache* command allows caching the Analysis metadata to a | ||
@@ -87,2 +76,8 @@ specific location. | ||
| BEWARE: The endpoint is experimental and can be interrupted at any time. | ||
| Further information | ||
| =================== | ||
| See: | ||
| https://lhcb-ap.docs.cern.ch/user_guide/accessing_output.html |
@@ -11,2 +11,9 @@ ############################################################################### | ||
| ############################################################################### | ||
| """Analysis Production Data package. | ||
| Programmatic interface to the Analysis Productions database, | ||
| that allows retrieving information about the samples produced. It queries a | ||
| REST endpoint provided by the Web application, and caches the data locally. | ||
| """ | ||
| __all__ = [ | ||
@@ -13,0 +20,0 @@ "AnalysisData", |
+170
-129
| ############################################################################### | ||
| # (c) Copyright 2021-2022 for the benefit of the LHCb Collaboration # | ||
| # (c) Copyright 2021-2023 for the benefit of the LHCb Collaboration # | ||
| # # | ||
@@ -11,3 +11,10 @@ # This software is distributed under the terms of the GNU General Public # | ||
| ############################################################################### | ||
| """Interface to the Analysis Production data. | ||
| Provides: | ||
| * the get_analysis_data method, the principal way to lookup AP info. It returns | ||
| and AnalysisData class. | ||
| * the AnalysisData class, which allows querying information about Analysis Productions | ||
| """ | ||
| import copy | ||
@@ -17,7 +24,9 @@ import itertools | ||
| import os | ||
| from pathlib import Path | ||
| from apd.ap_info import ( | ||
| InvalidCacheError, | ||
| SampleCollection, | ||
| check_tag_in_list, | ||
| fetch_ap_info, | ||
| cache_ap_info, | ||
| check_tag_value_possible, | ||
| iterable, | ||
@@ -33,5 +42,54 @@ load_ap_info, | ||
| APD_METADATA_LIFETIME = "APD_METADATA_LIFETIME" | ||
| APD_METADATA_LIFETIME_DEFAULT = 600 | ||
| APD_DATA_CACHE_DIR = "APD_DATA_CACHE_DIR" | ||
| def _load_and_setup_cache( | ||
| cache_dir, working_group, analysis, ap_date=None, api_url="https://lbap.app.cern.ch" | ||
| ): | ||
| """Utility function that checks whether the data for the Analysis | ||
| is cached already and does it if needed.""" | ||
| env_cache = os.environ.get(APD_METADATA_CACHE_DIR) | ||
| if not cache_dir: | ||
| if env_cache: | ||
| cache_dir = env_cache | ||
| else: | ||
| cache_dir = Path.home() / ".cache" / "apd" | ||
| logger.debug("Cache directory not set, using %s", cache_dir) | ||
| samples = None | ||
| try: | ||
| lifetime = os.environ.get(APD_METADATA_LIFETIME, APD_METADATA_LIFETIME_DEFAULT) | ||
| samples, _ = load_ap_info( | ||
| cache_dir, | ||
| working_group, | ||
| analysis, | ||
| ap_date=ap_date, | ||
| maxlifetime=lifetime, | ||
| ) | ||
| except FileNotFoundError: | ||
| logger.debug( | ||
| "Caching information for %s/%s to %s for time %s", | ||
| working_group, | ||
| analysis, | ||
| cache_dir, | ||
| ap_date, | ||
| ) | ||
| samples = cache_ap_info( | ||
| cache_dir, working_group, analysis, ap_date=ap_date, api_url=api_url | ||
| ) | ||
| except InvalidCacheError: | ||
| logger.debug( | ||
| "Invalid cache. reloading information for %s/%s to %s for time %s", | ||
| working_group, | ||
| analysis, | ||
| cache_dir, | ||
| ap_date, | ||
| ) | ||
| samples = cache_ap_info( | ||
| cache_dir, working_group, analysis, ap_date=ap_date, api_url=api_url | ||
| ) | ||
| assert samples is not None | ||
| return samples | ||
| def _validate_tags(tags, default_tags=None, available_tags=None): | ||
@@ -41,7 +99,20 @@ """Method that checks the dictionary of tag names, values that should be used | ||
| - Special cases are handled: tags "name" and "version" as well as "data" and "mc" | ||
| (which are converted to a "config" value). | ||
| - tag values cannot be None | ||
| - tag values cannot be of type bytes | ||
| - int tag values are converted to string | ||
| Note: | ||
| - Special cases are handled: tags "name" and "version" as well as "data" and "mc" | ||
| (which are converted to a "config" value). | ||
| - Tag values cannot be None. | ||
| - Tag values cannot be of type bytes. | ||
| - Int tag values are converted to string. | ||
| Args: | ||
| tags (dict): the dictionary of tags to be validated. | ||
| default_tags (dict, optional): provide default tags. Defaults to None. | ||
| available_tags (list, optional): provide a list of available tags. Defaults to None. | ||
| Raises: | ||
| ValueError: see the Note above. | ||
| TypeError: see the Note above. | ||
| Returns: | ||
| dict: the validated tags. | ||
| """ | ||
@@ -59,10 +130,2 @@ | ||
| # name and version are special tags in our case, we check their validity | ||
| if "name" in effective_tags: | ||
| raise ValueError("name is not a supported tag in AnalysisData objects") | ||
| version = effective_tags.get("version", None) | ||
| if version and iterable(version): | ||
| raise ValueError("version argument doesn't support iterables") | ||
| # Special handling for the data and mc tags to avoid having to | ||
@@ -120,3 +183,5 @@ # use the config tag | ||
| if available_tags is not None: | ||
| check_tag_in_list(t, available_tags) | ||
| # NB this raises an exception if the tag is not in the list | ||
| # or if the value does not match any samples | ||
| check_tag_value_possible(t, v, available_tags) | ||
| if isinstance(v, int) and not isinstance(v, bool): | ||
@@ -129,3 +194,3 @@ cleaned[t] = str(v) | ||
| def sample_check(samples, tags): | ||
| def _sample_check(samples, tags): | ||
| """Filter the SampleCollection and check that we have the | ||
@@ -168,2 +233,3 @@ samples that we expect""" | ||
| # Map contains AnalysisData objects already loaded | ||
| __analysis_map = {} | ||
@@ -181,3 +247,8 @@ | ||
| ): | ||
| """Cache with the same process""" | ||
| """Main method to get analysis production information. | ||
| Gets the AnalysisData information from the same process if possible. | ||
| If not loaded already, it loads it from the cache disk and if not present or valid, | ||
| fetches from the REST API. | ||
| """ | ||
| key = (working_group, analysis, ap_date) | ||
@@ -235,39 +306,32 @@ if key in __analysis_map: | ||
| """ | ||
| self.working_group = working_group | ||
| self.analysis = analysis | ||
| self._working_group = working_group | ||
| self._analysis = analysis | ||
| # self.samples is a SampleCollection filled in with the values | ||
| metadata_cache = metadata_cache or os.environ.get(APD_METADATA_CACHE_DIR, "") | ||
| need_clean_fetch = False | ||
| # self._samples is a SampleCollection filled in with the values | ||
| # Only for internal use as the default filters are NOT applied | ||
| self._samples = None | ||
| # Special case when the metadata cache is passed directly as | ||
| # a SampleCollection | ||
| if metadata_cache: | ||
| if isinstance(metadata_cache, SampleCollection): | ||
| logger.debug("Using SampleCollection passed to constructor") | ||
| self.samples = metadata_cache | ||
| else: | ||
| logger.debug("Using metadata cache %s", metadata_cache) | ||
| try: | ||
| self.samples, _ = load_ap_info( | ||
| metadata_cache, working_group, analysis, ap_date=ap_date | ||
| ) | ||
| except FileNotFoundError: | ||
| logger.debug( | ||
| "Could not find cache in %s, loading from remote", | ||
| metadata_cache, | ||
| ) | ||
| need_clean_fetch = True | ||
| self._samples = metadata_cache | ||
| else: | ||
| logger.debug( | ||
| "No cache set, fetching Analysis Production data from %s", api_url | ||
| ) | ||
| need_clean_fetch = True | ||
| # We use the env variable if it is set | ||
| envcache = os.environ.get(APD_METADATA_CACHE_DIR, None) | ||
| if envcache: | ||
| metadata_cache = envcache | ||
| if need_clean_fetch: | ||
| self.samples = fetch_ap_info( | ||
| working_group, analysis, None, api_url, ap_date=ap_date | ||
| if self._samples is None: | ||
| # In this case the metadata cache was not a SampleCollection or | ||
| # not set at all, set setup the cache | ||
| self._samples = _load_and_setup_cache( | ||
| metadata_cache, working_group, analysis, ap_date, api_url=api_url | ||
| ) | ||
| self.available_tags = self.samples.available_tags() | ||
| self._available_tags = self._samples.available_tags() | ||
| # Tags is a list of tags that can be used to restrict the samples that will be used | ||
| self.default_tags = _validate_tags(kwargs, available_tags=self.available_tags) | ||
| # "available_tags" is a list of tags that can be used to restrict the samples that will be used | ||
| self._default_tags = _validate_tags(kwargs, available_tags=self._available_tags) | ||
@@ -277,5 +341,5 @@ # Now dealing with data cache | ||
| if isinstance(data_cache, str): | ||
| self.data_cache = DataCache(data_cache) | ||
| self._data_cache = DataCache(data_cache) | ||
| else: | ||
| self.data_cache = data_cache | ||
| self._data_cache = data_cache | ||
@@ -285,4 +349,2 @@ def __call__( | ||
| *, | ||
| version=None, | ||
| name=None, | ||
| return_pfns=True, | ||
@@ -299,73 +361,45 @@ check_data=True, | ||
| # Cannot mix data from 2 versions in the same dataset | ||
| if not version: | ||
| version = self.default_tags.get("version", None) | ||
| if iterable(version): | ||
| raise ValueError("version argument doesn't support iterables") | ||
| # Establishing the list of samples to run on | ||
| samples = self.samples | ||
| samples = self._samples | ||
| if name: | ||
| if version: | ||
| # No need to apply other tags, this specifies explicitly a specific dataset | ||
| # We return it straight away | ||
| logger.debug("Filtering for version/name %s/%s", name, version) | ||
| samples = samples.filter("version", version) | ||
| if len(samples) == 0: | ||
| raise KeyError(f"No version {version}") | ||
| samples = samples.filter("name", name) | ||
| if len(samples) == 0: | ||
| raise KeyError(f"No name {name}") | ||
| else: | ||
| # We check whether a version was specified in the default tags | ||
| samples = samples.filter("name", name) | ||
| if len(samples) != 1: | ||
| raise ValueError( | ||
| f"{len(samples)} matching {name}, should be exactly 1" | ||
| ) | ||
| else: | ||
| # Merge the current tags with the default passed to the constructor | ||
| # and check that they are consistent | ||
| effective_tags = _validate_tags( | ||
| tags, self.default_tags, self.available_tags | ||
| ) | ||
| # Merge the current tags with the default passed to the constructor | ||
| # and check that they are consistent | ||
| effective_tags = _validate_tags(tags, self._default_tags, self._available_tags) | ||
| if version: | ||
| effective_tags["version"] = version | ||
| for tagname, tagvalue in effective_tags.items(): | ||
| logger.debug("Filtering for %s = %s", tagname, tagvalue) | ||
| for tagname, tagvalue in effective_tags.items(): | ||
| logger.debug("Filtering for %s = %s", tagname, tagvalue) | ||
| # Applying the filters in one go | ||
| samples = samples.filter(**effective_tags) | ||
| logger.debug("Matched %d samples", len(samples)) | ||
| # Applying the filters in one go | ||
| samples = samples.filter(**effective_tags) | ||
| logger.debug("Matched %d samples", len(samples)) | ||
| # Filter samples and check that we have what we expect | ||
| if check_data: | ||
| errors = _sample_check(samples, effective_tags) | ||
| if len(errors) > 0: | ||
| error_txt = f"{len(errors)} problem(s) found\n" | ||
| for etags, ecount in errors: | ||
| if etags: | ||
| error_txt += f"{str(etags)}: " | ||
| # Filter samples and check that we have what we expect | ||
| if check_data: | ||
| errors = sample_check(samples, effective_tags) | ||
| if len(errors) > 0: | ||
| error_txt = f"{len(errors)} problem(s) found\n" | ||
| for etags, ecount in errors: | ||
| if etags: | ||
| error_txt += f"{str(etags)}: " | ||
| if ecount > 0: | ||
| error_txt += f"{ecount} samples for the same configuration found, this is ambiguous:" | ||
| if ecount > 0: | ||
| error_txt += ( | ||
| f"(only the first {showmax} samples printed)" | ||
| if (ecount > showmax) | ||
| else "" | ||
| error_txt += ( | ||
| f"(only the first {showmax} samples printed)" | ||
| if (ecount > showmax) | ||
| else "" | ||
| ) | ||
| match_list = [ | ||
| str(m) | ||
| for m in itertools.islice( | ||
| samples.filter(**etags).itertags(), 0, showmax | ||
| ) | ||
| match_list = [ | ||
| str(m) | ||
| for m in itertools.islice( | ||
| samples.filter(**etags).itertags(), 0, showmax | ||
| ) | ||
| ] | ||
| error_txt += "".join( | ||
| ["\n" + " " * 5 + str(m) for m in match_list] | ||
| ) | ||
| logger.debug("Error loading data: %s", error_txt) | ||
| raise ValueError("Error loading data: " + error_txt) | ||
| ] | ||
| error_txt += "".join( | ||
| ["\n" + " " * 5 + str(m) for m in match_list] | ||
| ) | ||
| else: | ||
| error_txt += "No matching sample found" | ||
| logger.debug("Error loading data: %s", error_txt) | ||
| raise ValueError("Error loading data: " + error_txt) | ||
@@ -381,16 +415,18 @@ if return_pfns: | ||
| """Method to return PFNs, useful as it can be overriden in inheriting classes""" | ||
| if not self.data_cache: | ||
| if not self._data_cache: | ||
| return pfns | ||
| return [self.data_cache(pfn) for pfn in pfns] | ||
| return [self._data_cache(pfn) for pfn in pfns] | ||
| def __str__(self): | ||
| txt = f"AnalysisProductions: {self.working_group} / {self.analysis}\n" | ||
| txt += str(self.samples) | ||
| """User friendly representation of the AnalysisData instance.""" | ||
| txt = f"AnalysisProductions: {self._working_group} / {self._analysis}\n" | ||
| txt += str(self._samples) | ||
| return txt | ||
| def __repr__(self): | ||
| return f"<AnalysisData: WG={self.analysis}, analysis={self.working_group}, n_samples={len(self.samples)}>" | ||
| """String representation of the AnalysisData instance.""" | ||
| return f"<AnalysisData: WG={self._analysis}, analysis={self._working_group}, n_samples={len(self._samples)}>" | ||
| def summary(self, tags: list = None) -> dict: | ||
| """prepares a summary of the Analysis Production info""" | ||
| """Prepares a summary of the Analysis Production info.""" | ||
@@ -401,5 +437,5 @@ # Deal with the tags first | ||
| for tag in tags: | ||
| if tag in self.available_tags: | ||
| if tag in self._available_tags: | ||
| try: | ||
| values = sorted(self.available_tags[tag]) | ||
| values = sorted(self._available_tags[tag]) | ||
| except TypeError as exc: | ||
@@ -409,10 +445,10 @@ raise ValueError( | ||
| ) from exc | ||
| values = list(self.available_tags[tag]) | ||
| values = list(self._available_tags[tag]) | ||
| tag_summary[tag] = values | ||
| else: | ||
| raise ValueError( | ||
| f"Requested tag ({tag}) not valid for the given production (wg: {self.working_group}, analysis: {self.analysis})!" | ||
| f"Requested tag ({tag}) not valid for the given production (wg: {self._working_group}, analysis: {self._analysis})!" | ||
| ) | ||
| else: | ||
| tag_summary = dict(self.available_tags) | ||
| tag_summary = dict(self._available_tags) | ||
@@ -424,7 +460,12 @@ summary = {} | ||
| if not tags: | ||
| summary["analysis"] = self.analysis | ||
| summary["working_group"] = self.working_group | ||
| summary["Number_of_files"] = self.samples.file_count() | ||
| summary["Bytecount"] = self.samples.byte_count() | ||
| summary["analysis"] = self._analysis | ||
| summary["working_group"] = self._working_group | ||
| summary["Number_of_files"] = self._samples.file_count() | ||
| summary["Bytecount"] = self._samples.byte_count() | ||
| return summary | ||
| def all_samples(self): | ||
| """Returns all the samples in this Analysis Production. | ||
| i.e. without filtering by the default tags""" | ||
| return self._samples |
+54
-16
@@ -11,5 +11,9 @@ ############################################################################### | ||
| ############################################################################### | ||
| # | ||
| # Tool to load and interpret information from the AnalysisProductions data endpoint | ||
| # | ||
| """Internal tools to load and interpret information from the AnalysisProductions data endpoint. | ||
| This modules contains the retrieve the data from the AnalysisProductions endpoint | ||
| (with the APDataDownloader class). It returns JSON that can be loaded into a | ||
| SamplesCollection instance. | ||
| """ | ||
| import collections.abc | ||
@@ -36,3 +40,3 @@ import difflib | ||
| def iterable(arg): | ||
| """Version of Iterable that excludes str""" | ||
| """Version of Iterable that excludes str.""" | ||
| return isinstance(arg, collections.abc.Iterable) and not isinstance( | ||
@@ -44,3 +48,3 @@ arg, (str, bytes) | ||
| def safe_casefold(a): | ||
| """casefold that can be called on any type, does nothing on non str""" | ||
| """Casefold that can be called on any type, does nothing on non str.""" | ||
| if isinstance(a, str): | ||
@@ -51,4 +55,9 @@ return a.casefold() | ||
| def check_tag_in_list(tag, tag_list): | ||
| """Check if the tag exists in the list of whether there is one close enough""" | ||
| def check_tag_value_possible(tag, values, available_tags): | ||
| """Check if the `tag` exists in the in the `available_tags` of similar name, | ||
| in the sense of `difflib.get_close_matches`. If yes, also check that | ||
| the value is one of the existing tags values for that tag | ||
| """ | ||
| tag_list = [t.lower() for t in available_tags.keys()] | ||
| tag = safe_casefold(tag) | ||
| if tag not in tag_list: | ||
@@ -61,2 +70,21 @@ msg = f"Tag {tag} unknown." | ||
| raise ValueError(msg) | ||
| # Now we now the tag exists, checking the value is in the samples | ||
| # We can have either a value or a list passed, we always | ||
| # transform to a list to simplify processing | ||
| if not iterable(values): | ||
| values = [safe_casefold(str(values))] | ||
| else: | ||
| values = [safe_casefold(str(v)) for v in values] | ||
| for value in values: | ||
| possible_values = available_tags[tag] | ||
| if not iterable(possible_values): | ||
| possible_values = [possible_values] | ||
| possible_values = [safe_casefold(v) for v in possible_values] | ||
| if value not in possible_values: | ||
| msg = f"No sample for tag {tag}={value}" | ||
| closest = difflib.get_close_matches(value, possible_values, n=1) | ||
| if closest: | ||
| msg += f" Did you mean {closest[0]} ?" | ||
| msg += f"\nAvailable values for {tag}: {', '.join(possible_values)}" | ||
| raise ValueError(msg) | ||
@@ -69,3 +97,6 @@ | ||
| class APDataDownloader: | ||
| """Utility class that fetches the Analysis Production information.""" | ||
| def __init__(self, api_url="https://lbap.app.cern.ch"): | ||
| """Constructor defaulting to the production URL for lbap.""" | ||
| self.api_url = api_url | ||
@@ -140,3 +171,3 @@ self.token = None | ||
| ): | ||
| """Utils to compose the name of the cache files""" | ||
| """Utils to compose the name of the cache files.""" | ||
| cache_dir = Path(cache_dir) | ||
@@ -186,3 +217,3 @@ cache_dir = (cache_dir / "archives" / ap_date) if ap_date else cache_dir | ||
| def get_cache_age(cacheinfo): | ||
| def _get_cache_age(cacheinfo): | ||
| """Return the number of seconds since the cache was last modified""" | ||
@@ -205,3 +236,3 @@ modif_str = cacheinfo.get(MODIFY, None) | ||
| ): | ||
| """Fetch the AP info and cache it locally""" | ||
| """Fetch the AP info and cache it locally.""" | ||
| datafile, tagsfile, cacheinfofile = _analysis_files( | ||
@@ -226,8 +257,11 @@ cache_dir, working_group, analysis, ap_date | ||
| cacheinfo = _update_cache_info(cacheinfofile, False, True) | ||
| cache_age = get_cache_age(cacheinfo) | ||
| cache_age = _get_cache_age(cacheinfo) | ||
| logger.debug("cache_age: %s vs maxlife: %s", cache_age, maxlifetime) | ||
| # If we have specified a maxlifetime, we return an exception | ||
| # if the cache is too old | ||
| if maxlifetime: | ||
| if cache_age > float(maxlifetime): | ||
| if float(maxlifetime) >= 0 and cache_age > float(maxlifetime): | ||
| logger.debug( | ||
| "cache_too or no caching: %s vs maxlife: %s", cache_age, maxlifetime | ||
| ) | ||
| raise InvalidCacheError(f"Cache too old ({cache_age}s > {maxlifetime}s)") | ||
@@ -260,2 +294,3 @@ | ||
| def __len__(self): | ||
| """Returns the lenght of the samples list.""" | ||
| return len(self.info) | ||
@@ -277,2 +312,3 @@ | ||
| def __repr__(self): | ||
| """Create a string representation of the samples.""" | ||
| return "\n".join( | ||
@@ -287,2 +323,3 @@ [ | ||
| def __iter__(self): | ||
| """Iterate on the samples in the info member.""" | ||
| for s in self.info: | ||
@@ -292,2 +329,3 @@ yield s | ||
| def itertags(self): | ||
| """Iterate on all the tags present in all the samples.""" | ||
| for s in self.info: | ||
@@ -309,4 +347,2 @@ yield self._sampleTags(s) | ||
| """Utility method than handles specific tags, but not iterables""" | ||
| sampleTags = self._sampleTags(sample) | ||
| check_tag_in_list(ftag, sampleTags.keys()) | ||
| return safe_casefold(self._sampleTags(sample).get(ftag)) == safe_casefold( | ||
@@ -368,2 +404,3 @@ fvalue | ||
| def __or__(self, samples): | ||
| """Logical or between two SampleCollections.""" | ||
| info = self.info + samples.info | ||
@@ -397,3 +434,4 @@ tags = {**(self.tags), **(samples.tags)} | ||
| """Tool that takes the samples and groups them by the tags specified. | ||
| If no list of tags is specified, then the existing ones are used""" | ||
| If no list of tags is specified, then the existing ones are used. | ||
| """ | ||
@@ -400,0 +438,0 @@ report = self.report() |
+8
-62
@@ -19,3 +19,2 @@ ############################################################################### | ||
| import tempfile | ||
| from pathlib import Path | ||
@@ -26,10 +25,4 @@ import click | ||
| from .analysis_data import ( | ||
| APD_DATA_CACHE_DIR, | ||
| APD_METADATA_CACHE_DIR, | ||
| APD_METADATA_LIFETIME, | ||
| AnalysisData, | ||
| get_analysis_data, | ||
| ) | ||
| from .ap_info import InvalidCacheError, cache_ap_info, load_ap_info | ||
| from .analysis_data import APD_DATA_CACHE_DIR, APD_METADATA_CACHE_DIR, get_analysis_data | ||
| from .ap_info import cache_ap_info | ||
| from .authentication import get_auth_headers, logout | ||
@@ -78,38 +71,2 @@ from .data_cache import DataCache | ||
| def setup_cache(cache_dir, working_group, analysis, ap_date=None): | ||
| """Utility function that checks whether the data for the Analysis | ||
| is cached already and does it if needed.""" | ||
| if not cache_dir: | ||
| cache_dir = Path.home() / ".cache" / "apd" | ||
| logger.debug("Cache directory not set, using %s", cache_dir) | ||
| try: | ||
| lifetime = os.environ.get(APD_METADATA_LIFETIME, None) | ||
| load_ap_info( | ||
| cache_dir, | ||
| working_group, | ||
| analysis, | ||
| ap_date=ap_date, | ||
| maxlifetime=lifetime, | ||
| ) | ||
| except FileNotFoundError: | ||
| logger.debug( | ||
| "Caching information for %s/%s to %s for time %s", | ||
| working_group, | ||
| analysis, | ||
| cache_dir, | ||
| ap_date, | ||
| ) | ||
| cache_ap_info(cache_dir, working_group, analysis, ap_date=ap_date) | ||
| except InvalidCacheError: | ||
| logger.debug( | ||
| "Invalid cache. reloading information for %s/%s to %s for time %s", | ||
| working_group, | ||
| analysis, | ||
| cache_dir, | ||
| ap_date, | ||
| ) | ||
| cache_ap_info(cache_dir, working_group, analysis, ap_date=ap_date) | ||
| return cache_dir | ||
| def _process_common_tags(eventtype, datatype, polarity, config, name, version): | ||
@@ -171,3 +128,3 @@ """Util to simplify the parsing of common tags""" | ||
| logger.debug( | ||
| "Caching information for %s/%s to %s for time %s", | ||
| "Caching %s/%s to %s for time %s", | ||
| working_group, | ||
@@ -230,4 +187,2 @@ analysis, | ||
| cache_dir = setup_cache(cache_dir, working_group, analysis, date) | ||
| # Loading the data and filtering/displaying | ||
@@ -294,5 +249,2 @@ datasets = get_analysis_data( | ||
| # Dealing with the cache | ||
| cache_dir = setup_cache(cache_dir, working_group, analysis, date) | ||
| # Loading the data and filtering/displaying | ||
@@ -332,7 +284,4 @@ datasets = get_analysis_data( | ||
| # Dealing with the cache | ||
| setup_cache(cache_dir, working_group, analysis) | ||
| # Loading the data first | ||
| datasets = AnalysisData(working_group, analysis, metadata_cache=cache_dir) | ||
| datasets = get_analysis_data(working_group, analysis, metadata_cache=cache_dir) | ||
@@ -346,3 +295,3 @@ # Checking whether we need to group the data... | ||
| groups = datasets.samples.groupby(groupby_tags) | ||
| groups = datasets.all_samples().groupby(groupby_tags) | ||
| if output: | ||
@@ -357,3 +306,3 @@ with open(output, "w") as f: | ||
| else: | ||
| report = datasets.samples.report() | ||
| report = datasets.all_samples().report() | ||
| # gets the report as CSV in this case, not JSON | ||
@@ -374,3 +323,3 @@ report_str = "\n".join(([",".join([str(e) for e in line]) for line in report])) | ||
| default=os.environ.get(APD_METADATA_CACHE_DIR, None), | ||
| help="Specify location of the cached analysis data files", | ||
| help="Specify location of the cached analysis metadata", | ||
| ) | ||
@@ -394,4 +343,2 @@ @click.option( | ||
| """Print a summary of the information available about the specified analysis.""" | ||
| # Dealing with the cache | ||
| cache_dir = setup_cache(cache_dir, working_group, analysis, date) | ||
@@ -422,3 +369,3 @@ # Loading the dataset and displaying its summary | ||
| default=os.environ.get(APD_DATA_CACHE_DIR, None), | ||
| help="Specify location of the cache for the analysis metadata", | ||
| help="Specify location where a copy of the files will be kept", | ||
| ) | ||
@@ -476,3 +423,2 @@ @click.option( | ||
| # pylint: disable-msg=too-many-locals | ||
| cache_dir = setup_cache(cache_dir, working_group, analysis, date) | ||
@@ -479,0 +425,0 @@ if not data_cache_dir: |
@@ -56,3 +56,3 @@ ############################################################################### | ||
| cmd = ["xrdcp", "--silent", str(remote), str(local)] | ||
| result = subprocess.run(cmd, check=True, timeout=1000) | ||
| result = subprocess.run(cmd, check=True) | ||
| return result |
@@ -18,3 +18,3 @@ ############################################################################### | ||
| from apd.analysis_data import APD_METADATA_CACHE_DIR | ||
| from apd.analysis_data import APD_METADATA_CACHE_DIR, APD_METADATA_LIFETIME | ||
| from apd.ap_info import cache_ap_info, load_ap_info_from_single_file | ||
@@ -100,2 +100,3 @@ | ||
| monkeypatch.setenv(APD_METADATA_CACHE_DIR, str(Path(__file__).parent / "cache-dir")) | ||
| monkeypatch.setenv(APD_METADATA_LIFETIME, "-1") | ||
| yield |
| ############################################################################### | ||
| # (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # | ||
| # (c) Copyright 2021-2023 CERN for the benefit of the LHCb Collaboration # | ||
| # # | ||
@@ -11,6 +11,12 @@ # This software is distributed under the terms of the GNU General Public # | ||
| ############################################################################### | ||
| import logging | ||
| import pytest | ||
| from apd import AnalysisData | ||
| from apd.analysis_data import APD_METADATA_CACHE_DIR, sample_check | ||
| from apd.analysis_data import ( | ||
| APD_METADATA_CACHE_DIR, | ||
| APD_METADATA_LIFETIME, | ||
| _sample_check, | ||
| ) | ||
@@ -26,3 +32,6 @@ | ||
| def test_fromendpoint(monkeypatch, mocked_responses): | ||
| logger = logging.getLogger("apd") | ||
| logger.setLevel(logging.DEBUG) | ||
| monkeypatch.setenv(APD_METADATA_CACHE_DIR, "") | ||
| monkeypatch.setenv(APD_METADATA_LIFETIME, "0") | ||
| datasets = AnalysisData("SL", "RDs") | ||
@@ -58,3 +67,3 @@ assert len(datasets(datatype="2012", polarity=["magup", "magdown"])) == 11 | ||
| ): | ||
| sample_check(samples, tags) | ||
| _sample_check(samples, tags) | ||
@@ -65,3 +74,3 @@ | ||
| samples = apinfo_multipleversions.filter(**tags) | ||
| errors = sample_check(samples, tags) | ||
| errors = _sample_check(samples, tags) | ||
| assert len(errors) == 0 | ||
@@ -73,3 +82,3 @@ | ||
| samples = apinfo_multipleversions.filter(**tags) | ||
| errors = sample_check(samples, tags) | ||
| errors = _sample_check(samples, tags) | ||
| assert len(errors) == 1 | ||
@@ -108,1 +117,8 @@ | ||
| AnalysisData("SL", "RDs", metadata_cache=apinfo_cache, badtag="novalue") | ||
| def test_badtagvalue_constructor(apinfo_cache): | ||
| """In case we specify a tag with a value which does not exist, we should raise | ||
| and exception in that case""" | ||
| with pytest.raises(ValueError): | ||
| AnalysisData("SL", "RDs", metadata_cache=apinfo_cache, datatype="2032") |
+15
-55
@@ -29,8 +29,2 @@ ############################################################################### | ||
| with pytest.raises(ValueError, match="version argument doesn't support iterables"): | ||
| datasets(version=["v0r0p2518507", "v0r0p2970193"], name="2018_15164022_magup") | ||
| with pytest.raises(ValueError, match="version argument doesn't support iterables"): | ||
| datasets(version={"v0r0p2518507", "v0r0p2970193"}, name="2018_15164022_magup") | ||
| pfns = set( | ||
@@ -62,30 +56,2 @@ datasets( | ||
| def test_defaults_version(apd_cache): | ||
| datasets = AnalysisData("b2oc", "b02dkpi", version="v0r0p2518507") | ||
| pfns = datasets(name="2018_15164022_magup") | ||
| assert len(pfns) == 6 | ||
| datasets = AnalysisData("b2oc", "b02dkpi", version="v0r0p2970193") | ||
| pfns = datasets(name="2018_15164022_magup") | ||
| assert len(pfns) == 1 and "00145075_00000001" in pfns[0] | ||
| with pytest.raises(ValueError, match="version argument doesn't support iterables"): | ||
| AnalysisData("b2oc", "b02dkpi", version=["v0r0p2518507", "v0r0p2970193"]) | ||
| with pytest.raises(ValueError, match="version argument doesn't support iterables"): | ||
| AnalysisData("b2oc", "b02dkpi", version={"v0r0p2518507", "v0r0p2970193"}) | ||
| def test_defaults_name(apd_cache): | ||
| with pytest.raises( | ||
| ValueError, match="name is not a supported tag in AnalysisData objects" | ||
| ): | ||
| AnalysisData( | ||
| "b2oc", "b02dkpi", version="v0r0p2518507", name="2018_15164022_magup" | ||
| ) | ||
| with pytest.raises( | ||
| ValueError, match="name is not a supported tag in AnalysisData objects" | ||
| ): | ||
| AnalysisData("b2oc", "b02dkpi", name="2018_15164022_magup") | ||
| def test_defaults_tag_override(apd_cache): | ||
@@ -176,5 +142,3 @@ datasets = AnalysisData( | ||
| with pytest.raises( | ||
| ValueError, match=r"Error loading data: 2 problem\(s\) found" | ||
| ): # TODO: This should be more specific | ||
| with pytest.raises(ValueError, match=r"No sample for tag polarity=magoff.*"): | ||
| datasets(datatype=["2015", "2016"], polarity=["magoff"]) | ||
@@ -212,22 +176,6 @@ | ||
| def test_missing_by_name(apd_cache): | ||
| datasets = AnalysisData("b2oc", "b02dkpi") | ||
| with pytest.raises(KeyError): | ||
| datasets(version="i-do-no-exist", name="2018_15164022_magup") | ||
| with pytest.raises(KeyError): | ||
| datasets(version="v0r0p2970193", name="i-do-not-exist") | ||
| def test_missing_by_tags(apd_cache): | ||
| datasets = AnalysisData("b2oc", "b02dkpi") | ||
| with pytest.raises(KeyError): | ||
| datasets(version="i-do-no-exist", name="2018_15164022_magup") | ||
| with pytest.raises(KeyError): | ||
| datasets(version="v0r0p2970193", name="i-do-not-exist") | ||
| def test_analysis_case_sensitivity(apd_cache): | ||
| datasets = AnalysisData("b2oc", "b02dkpi") | ||
| assert len(datasets.samples) == 1694 | ||
| assert len(AnalysisData("B2OC", "B02DKPI").samples) == 1694 | ||
| assert len(datasets._samples) == 1694 | ||
| assert len(AnalysisData("B2OC", "B02DKPI")._samples) == 1694 | ||
@@ -245,1 +193,13 @@ pfns = datasets(version="v0r0p2970193", name="2018_15164022_magup") | ||
| assert len(pfns) == 1 and "00145075_00000001" in pfns[0] | ||
| def test_unknown_tag_value(apd_cache): | ||
| datasets = AnalysisData("b2oc", "b02dkpi") | ||
| with pytest.raises(ValueError, match="No sample for tag datatype=2032.*"): | ||
| datasets( | ||
| datatype=["2032"], | ||
| polarity=["magup", "magdown"], | ||
| eventtype="11164047", | ||
| mc=True, | ||
| ) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
54878
0.02%2999096
-0.12%