codecov[bot]: # [Codecov](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=h1) Report
> Merging [#468](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=desc) into [master](https://codecov.io/gh/regro/regolith/commit/26ad556f36eb83cc153ec26c567e5f0992a17dfc&el=desc) will **increase** coverage by `0.04%`.
> The diff coverage is `84.61%`.

[![Impacted file tree graph](https://codecov.io/gh/regro/regolith/pull/468/graphs/tree.svg?width=650&height=150&src=pr&token=YOWfAqEPVS)](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #468      +/-   ##
==========================================
+ Coverage   63.89%   63.94%   +0.04%     
==========================================
  Files          54       54              
  Lines        4878     4890      +12     
==========================================
+ Hits         3117     3127      +10     
- Misses       1761     1763       +2     
```


| [Impacted Files](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [regolith/helpers/l\_projectahelper.py](https://codecov.io/gh/regro/regolith/pull/468/diff?src=pr&el=tree#diff-cmVnb2xpdGgvaGVscGVycy9sX3Byb2plY3RhaGVscGVyLnB5) | `87.06% <84.61%> (-0.44%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=footer). Last update [26ad556...7ccbd40](https://codecov.io/gh/regro/regolith/pull/468?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

mwinitch: @sbillinge This is ready for review and merge
mwinitch: @sbillinge This is ready for review and merge
mwinitch: @sbillinge This is ready for review and merge
diff --git a/news/grp_by_lead.rst b/news/grp_by_lead.rst
new file mode 100644
index 00000000..4de9662e
--- /dev/null
+++ b/news/grp_by_lead.rst
@@ -0,0 +1,12 @@
+**Added:**
+    * New flag to l_projecta called --grp_by_lead that groups all projectums by their leads
+
+**Changed:** None
+
+**Deprecated:** None
+
+**Removed:** None
+
+**Fixed:** None
+
+**Security:** None
\ No newline at end of file
diff --git a/regolith/helpers/l_projectahelper.py b/regolith/helpers/l_projectahelper.py
index 65a1ca4c..17b41f61 100644
--- a/regolith/helpers/l_projectahelper.py
+++ b/regolith/helpers/l_projectahelper.py
@@ -49,6 +49,8 @@ def subparser(subpi):
     subpi.add_argument("-g", "--grant",
                        help="Filter projecta by a grant ID"
                        )
+    subpi.add_argument("--grp_by_lead", action='store_true',
+                       help="Lists all projecta by their lead")
     subpi.add_argument("-f", "--filter", nargs="+",
                        help="Search this collection by giving key element pairs"
                        )
@@ -99,7 +101,8 @@ class ProjectaListerHelper(SoutHelperBase):
             results = search_collection(self.gtx["projecta"], rc.filter, rc.keys)
             print(results, end="")
             return
-        if (not rc.lead) and (not rc.person) and (not rc.ended) and (not rc.grant) and (not rc.verbose):
+
+        if (not rc.lead) and (not rc.person) and (not rc.ended) and (not rc.grant) and (not rc.verbose) and (not rc.grp_by_lead):
             return
         if rc.date:
             desired_date = date_parser.parse(rc.date).date()
@@ -114,9 +117,16 @@ class ProjectaListerHelper(SoutHelperBase):
         bad_stati = ["finished", "cancelled", "paused", "back_burner"]
         projecta = []
         end_projecta = []
+        grouped_projecta = {}
         if rc.lead and rc.person:
             raise RuntimeError(f"please specify either lead or person, not both")
         for projectum in self.gtx["projecta"]:
+            if rc.grp_by_lead:
+                if projectum.get('lead') not in grouped_projecta:
+                    grouped_projecta[projectum.get('lead')] = [projectum.get('_id')]
+                else:
+                    grouped_projecta[projectum.get('lead')].append(projectum.get('_id'))
+                continue
             if isinstance(projectum.get('group_members'), str):
                 projectum['group_members'] = [projectum.get('group_members')]
             if rc.lead and projectum.get('lead') != rc.lead:
@@ -162,6 +172,11 @@ class ProjectaListerHelper(SoutHelperBase):
                                                                                           p.get("description"),
                                                                                           p.get("lead"), members,
                                                                                           collaborators))
+        if rc.grp_by_lead:
+            for key, values in grouped_projecta.items():
+                print(f"{key}:")
+                for v in values:
+                    print(f"    {v}")
         projecta.sort()
         for i in projecta:
             print(i)

ENH: l_projecta group by lead
desired behavior:
when --grp_by_lead is specified, output lools like:
```
sbillinge:
   20sb_prum1
   20sb_prum2
astudent:
   20as_prum1
   20as_prum2
```
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 5c073952..736fa917 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -39,6 +39,9 @@ helper_map = [
     (["helper", "l_projecta", "--grant", "SymPy-1.1"],
      "20ly_newprojectum\n20sb_firstprojectum\n"
      ),
+    (["helper", "l_projecta", "--grp_by_lead"],
+     "lyang:\n    20ly_newprojectum\nascopatz:\n    20sb_firstprojectum\n"
+     ),
     (["helper", "l_projecta", "--ended", "-d", "2020-06-02"],
      "20sb_firstprojectum    My first projectum\n    Lead: ascopatz    Members: ascopatz    Collaborators: aeinstein, pdirac\n"
      ),

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.8
requirements/run.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
Cerberus==1.3.7
dnspython==2.6.1
docutils==0.20.1
et_xmlfile==2.0.0
exceptiongroup==1.2.2
iniconfig==2.1.0
Jinja2==3.1.6
MarkupSafe==2.1.5
nameparser==1.1.3
numpy==1.24.4
openpyxl==3.1.5
packaging==24.2
pandas==2.0.3
pluggy==1.5.0
pymongo==4.10.1
pytest==8.3.5
python-dateutil==2.9.0.post0
pytz==2025.2
-e git+https://github.com/regro/regolith.git@81eec09565b0dc43b60db007c7a885c18c1e0f0e#egg=regolith
rever==0.3.3
ruamel.yaml==0.18.10
ruamel.yaml.clib==0.2.8
six==1.17.0
tomli==2.2.1
tzdata==2025.2
xonsh==0.14.0

name: regolith
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=24.2=py38h06a4308_0
  - python=3.8.20=he870216_0
  - readline=8.2=h5eee18b_0
  - setuptools=75.1.0=py38h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.44.0=py38h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cerberus==1.3.7
      - dnspython==2.6.1
      - docutils==0.20.1
      - et-xmlfile==2.0.0
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - jinja2==3.1.6
      - markupsafe==2.1.5
      - nameparser==1.1.3
      - numpy==1.24.4
      - openpyxl==3.1.5
      - packaging==24.2
      - pandas==2.0.3
      - pluggy==1.5.0
      - pymongo==4.10.1
      - pytest==8.3.5
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - rever==0.3.3
      - ruamel-yaml==0.18.10
      - ruamel-yaml-clib==0.2.8
      - six==1.17.0
      - tomli==2.2.1
      - tzdata==2025.2
      - xonsh==0.14.0
prefix: /opt/conda/envs/regolith

tests/test_helpers.py::test_helper_python[hm11]
tests/test_helpers.py::test_helper_python[hm4]
tests/test_helpers.py::test_helper_python[hm0] tests/test_helpers.py::test_helper_python[hm1] tests/test_helpers.py::test_helper_python[hm2] tests/test_helpers.py::test_helper_python[hm3] tests/test_helpers.py::test_helper_python[hm5] tests/test_helpers.py::test_helper_python[hm6] tests/test_helpers.py::test_helper_python[hm7] tests/test_helpers.py::test_helper_python[hm8] tests/test_helpers.py::test_helper_python[hm9] tests/test_helpers.py::test_helper_python[hm10] tests/test_helpers.py::test_helper_python[hm12] tests/test_helpers.py::test_helper_python[hm13] tests/test_helpers.py::test_helper_python[hm14] tests/test_helpers.py::test_helper_python[hm15] tests/test_helpers.py::test_helper_python[hm16] tests/test_helpers.py::test_helper_python[hm17] tests/test_helpers.py::test_helper_python[hm18] tests/test_helpers.py::test_helper_python[hm19] tests/test_helpers.py::test_helper_python[hm20] tests/test_helpers.py::test_helper_python[hm21] tests/test_helpers.py::test_helper_python[hm22] tests/test_helpers.py::test_helper_python[hm23] tests/test_helpers.py::test_helper_python[hm24]
news/grp_by_lead.rst regolith/helpers/l_projectahelper.py
news/grp_by_lead.rst regolith/helpers/l_projectahelper.py