Skip to content

Commit 1099bde

Browse files
committed
Merge branch 'feature/PI-631-generate_product_ids' into release/2024-12-05
2 parents 7ebc04d + 7c0e54f commit 1099bde

File tree

7 files changed

+216
-1
lines changed

7 files changed

+216
-1
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
4. [Workflow](#workflow)
2222
5. [Swagger](#swagger)
2323
6. [ETL](#etl)
24+
7. [Administration](#administration)
2425

2526
---
2627

@@ -429,6 +430,18 @@ and
429430
make etl--clear-state WORKSPACE=dev SET_CHANGELOG_NUMBER=540210
430431
```
431432

433+
## Administration
434+
435+
### Generating Ids
436+
437+
In order to generate a persistent list of Ids across environments then run... (The example given will generate 100 ids.)
438+
439+
```
440+
make admin--generate-ids--product SET_GENERATOR_COUNT=100
441+
```
442+
443+
Any previously generated ids will not be overwritten.
444+
432445
### Documentation
433446

434447
We have several locations for the Swagger to keep things as visible as possible

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ boto3-stubs = { extras = [
5656
], version = "^1.34.37" }
5757
pytest-timeout = "^2.2.0"
5858
lz4 = "^4.3.3"
59+
pytest-repeat = "^0.9.3"
5960

6061
[tool.poetry.group.local]
6162
optional = true
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SET_GENERATOR_COUNT :=
2+
3+
admin--generate-ids--product: ## Generate product Ids
4+
poetry run python scripts/administration/id_generator.py --count="$(SET_GENERATOR_COUNT)"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import argparse
2+
import json
3+
import os
4+
5+
from domain.core.cpm_system_id import ProductId
6+
7+
ID_FILE_MAP = {
8+
"product": f"{os.getcwd()}/src/layers/domain/core/cpm_system_id/generated_ids/product_ids.json",
9+
}
10+
11+
ID_CLASS_MAP = {
12+
"product": ProductId,
13+
}
14+
15+
16+
def generator(id_type="product"):
17+
id_class = ID_CLASS_MAP.get(id_type)
18+
generator = id_class.create()
19+
return generator.__root__
20+
21+
22+
def open_file(id_type="product"):
23+
if os.path.exists(ID_FILE_MAP.get(id_type)):
24+
with open(ID_FILE_MAP.get(id_type), "r") as file:
25+
return set(json.load(file))
26+
return set()
27+
28+
29+
def save_file(ids, id_type="product"):
30+
with open(ID_FILE_MAP.get(id_type), "w") as file:
31+
json.dump(list(ids), file)
32+
33+
34+
def bulk_generator(id_count=1, id_type="product"):
35+
ids = open_file(id_type=id_type)
36+
starting_id_count = len(ids)
37+
while len(ids) - starting_id_count < id_count:
38+
ids.add(generator())
39+
save_file(ids, id_type)
40+
return ids
41+
42+
43+
if __name__ == "__main__":
44+
parser = argparse.ArgumentParser(description="Generate unique Product Ids.")
45+
parser.add_argument(
46+
"--count", type=int, default=1, help="Number of Product Ids to generate."
47+
)
48+
args = parser.parse_args()
49+
ids = bulk_generator(args.count)
50+
id_type = "product"
51+
print(f"Generated {id_type.capitalize()} Ids...") # noqa
52+
print("========================") # noqa
53+
for id in ids:
54+
print(id) # noqa
55+
56+
print("========================") # noqa
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[
2+
"P.RKK-P46",
3+
"P.MNE-V6V",
4+
"P.HPM-9MC",
5+
"P.PLT-9YV",
6+
"P.46W-MHJ",
7+
"P.7JD-MUC",
8+
"P.3GG-DW6",
9+
"P.AE9-96P",
10+
"P.K6N-D4X",
11+
"P.RVC-APA",
12+
"P.JFX-3M4",
13+
"P.HKM-MGU",
14+
"P.NXD-R7N",
15+
"P.66R-VWJ",
16+
"P.D3Y-H7J",
17+
"P.T46-YEE",
18+
"P.XTW-LG3",
19+
"P.WTJ-FJT",
20+
"P.XWA-VFF",
21+
"P.6YL-D93",
22+
"P.CPY-J4W",
23+
"P.JNH-MWV",
24+
"P.K7C-X7D",
25+
"P.MLH-97C",
26+
"P.PV9-GTU",
27+
"P.JUW-CRP",
28+
"P.HUX-WFD",
29+
"P.9RJ-VTW",
30+
"P.EGF-DHU",
31+
"P.XMX-4CN",
32+
"P.4RE-CKC",
33+
"P.HR9-9PU",
34+
"P.U9F-D3X",
35+
"P.49J-A7U",
36+
"P.JV6-H4R",
37+
"P.THY-GVT",
38+
"P.AY3-ERX",
39+
"P.6DP-3MK",
40+
"P.DUG-WPW",
41+
"P.DKM-L96",
42+
"P.CXW-CRT",
43+
"P.EKF-XRK",
44+
"P.JG7-LCA",
45+
"P.LRN-UWP",
46+
"P.9AK-LMH",
47+
"P.K96-UND",
48+
"P.YUP-T9J",
49+
"P.EJK-ACK",
50+
"P.H9X-PD4",
51+
"P.VKF-LUV",
52+
"P.HWC-GLY",
53+
"P.D3C-YW7",
54+
"P.KM7-439",
55+
"P.RK9-CKM",
56+
"P.CCW-G73",
57+
"P.RYF-MMM",
58+
"P.4CU-UKP",
59+
"P.GV7-FPE",
60+
"P.AEP-C4V",
61+
"P.CTC-UEM",
62+
"P.DYD-3RG",
63+
"P.H6K-7VL",
64+
"P.U6J-EEU",
65+
"P.EWX-ACY",
66+
"P.4HC-WJN",
67+
"P.A4C-EKV",
68+
"P.6AK-96A",
69+
"P.L9N-Y3T",
70+
"P.TVU-MVK",
71+
"P.WW7-FCD",
72+
"P.PPW-73K",
73+
"P.JPT-6AY",
74+
"P.G9T-UAR",
75+
"P.6LF-D3M",
76+
"P.4NT-EGL",
77+
"P.7JR-XAP",
78+
"P.PEH-UFM",
79+
"P.6KC-WPU",
80+
"P.464-URD",
81+
"P.3DR-EK3",
82+
"P.KG3-CHW",
83+
"P.3FL-PTA",
84+
"P.GT4-9DY",
85+
"P.9RW-9FX",
86+
"P.HTJ-YFF",
87+
"P.NMX-MHC",
88+
"P.4KJ-DHT",
89+
"P.3KL-X6F",
90+
"P.DUM-YKT",
91+
"P.F7G-GVH",
92+
"P.4H4-4FJ",
93+
"P.9XP-NX6",
94+
"P.4VH-YLN",
95+
"P.VRA-F9U",
96+
"P.97J-HN9",
97+
"P.3WF-CNA",
98+
"P.TUR-X4P",
99+
"P.DXE-FWE",
100+
"P.C9W-CLJ",
101+
"P.JVG-CA4"
102+
]

src/layers/domain/core/cpm_system_id/tests/test_cpm_system_id_v1.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1+
import os
2+
from pathlib import Path
3+
14
import pytest
25
from domain.core.cpm_system_id import AsidId, PartyKeyId, ProductId
6+
from event.json import json_load
7+
8+
PATH_TO_CPM_SYSTEM_IDS = Path(__file__).parent.parent
9+
PRODUCT_IDS_GENERATED_FILE = f"{PATH_TO_CPM_SYSTEM_IDS}/generated_ids/product_ids.json"
10+
generated_product_ids = set()
11+
12+
13+
@pytest.fixture(scope="module")
14+
def _get_generated_ids():
15+
global generated_product_ids
16+
if os.path.exists(PRODUCT_IDS_GENERATED_FILE):
17+
with open(PRODUCT_IDS_GENERATED_FILE, "r") as file:
18+
generated_product_ids = set(json_load(file))
319

420

521
def test_party_key_generator_format_key():
@@ -71,9 +87,11 @@ def test_asid_generator_increment_number():
7187
assert generator.id == "223456789013"
7288

7389

74-
def test_product_id_generator_format_key():
90+
@pytest.mark.repeat(50)
91+
def test_product_id_generator_format_key(_get_generated_ids):
7592
generator = ProductId.create()
7693
assert generator.id is not None
94+
assert generator.id not in generated_product_ids
7795

7896

7997
@pytest.mark.parametrize(

src/layers/domain/core/cpm_system_id/v1.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
import os
12
import random
23
import re
34
from abc import ABC, abstractmethod
45
from datetime import datetime
6+
from functools import cache
7+
from pathlib import Path
58
from uuid import uuid4
69

710
from domain.core.base import BaseModel
811
from domain.core.device_key import validate_key
912
from domain.core.error import InvalidKeyPattern
1013
from domain.core.product_key import ProductKeyType
14+
from event.json import json_load
1115
from pydantic import validator
1216

1317
FIRST_ASID = 200000099999
@@ -19,11 +23,22 @@
1923
PRODUCT_ID_PATTERN = re.compile(
2024
rf"^P\.[{PRODUCT_ID_VALID_CHARS}]{{{PRODUCT_ID_PART_LENGTH}}}-[{PRODUCT_ID_VALID_CHARS}]{{{PRODUCT_ID_PART_LENGTH}}}$"
2125
)
26+
27+
PATH_TO_CPM_SYSTEM_IDS = Path(__file__).parent
28+
PRODUCT_IDS_GENERATED_FILE = f"{PATH_TO_CPM_SYSTEM_IDS}/generated_ids/product_ids.json"
2229
PRODUCT_TEAM_ID_PATTERN = re.compile(
2330
r"^[a-zA-Z0-9]+\.([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$"
2431
)
2532

2633

34+
@cache
35+
def _load_existing_ids():
36+
if os.path.exists(PRODUCT_IDS_GENERATED_FILE):
37+
with open(PRODUCT_IDS_GENERATED_FILE, "r") as file:
38+
return set(json_load(file))
39+
return set()
40+
41+
2742
class CpmSystemId(BaseModel, ABC):
2843
__root__: str = None
2944

@@ -111,13 +126,19 @@ def create(cls):
111126
"".join(rng.choices(PRODUCT_ID_VALID_CHARS, k=PRODUCT_ID_PART_LENGTH))
112127
for _ in range(PRODUCT_ID_NUMBER_OF_PARTS)
113128
)
129+
if f"P.{product_id}" in cls.load_existing_ids():
130+
return cls.create()
114131
return cls(__root__=f"P.{product_id}")
115132

116133
@classmethod
117134
def validate_cpm_system_id(cls, cpm_system_id: str) -> bool:
118135
"""Validate that the ProductId has the correct format."""
119136
return PRODUCT_ID_PATTERN.match(cpm_system_id) is not None
120137

138+
@classmethod
139+
def load_existing_ids(cls):
140+
return _load_existing_ids()
141+
121142

122143
class ProductTeamId(CpmSystemId):
123144
@classmethod

0 commit comments

Comments
 (0)