Skip to content

Commit 034b399

Browse files
pattisdradamsachsRachel Silver
authored
Base TCF v3 Mapping (#167)
Co-authored-by: Adam Sachs <[email protected]> Co-authored-by: Rachel Silver <[email protected]>
1 parent 1da0a0c commit 034b399

File tree

8 files changed

+399
-1
lines changed

8 files changed

+399
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ The types of changes are:
1616

1717
## [Unreleased](https://github.com/ethyca/fideslang/compare/2.0.4...main)
1818

19+
### Added
20+
21+
- Added GVL mappings and utility functions [#167](https://github.com/ethyca/fideslang/pull/167)
1922

2023

2124
## [2.0.4](https://github.com/ethyca/fideslang/compare/2.0.3...2.0.4)

src/fideslang/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@
99

1010
from ._version import __version__
1111

12+
# export our GVL utilities
13+
from .gvl import (
14+
GVL_PURPOSES,
15+
GVL_SPECIAL_PURPOSES,
16+
MAPPED_PURPOSES,
17+
MAPPED_PURPOSES_BY_DATA_USE,
18+
MAPPED_SPECIAL_PURPOSES,
19+
data_use_to_purpose,
20+
purpose_to_data_use,
21+
)
22+
1223
# Export the Models
1324
from .models import (
1425
DataCategory,

src/fideslang/gvl/__init__.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import os
2+
from json import load
3+
from os.path import dirname, join
4+
from typing import Dict, List, Optional
5+
6+
from .models import Feature, MappedPurpose, Purpose
7+
8+
PURPOSE_MAPPING_FILE = join(
9+
dirname(__file__),
10+
"",
11+
"gvl_data_use_mapping.json",
12+
)
13+
14+
FEATURE_MAPPING_FILE = join(
15+
dirname(__file__),
16+
"",
17+
"gvl_feature_mapping.json",
18+
)
19+
20+
21+
GVL_PURPOSES: Dict[int, Purpose] = {}
22+
MAPPED_PURPOSES: Dict[int, MappedPurpose] = {}
23+
24+
GVL_SPECIAL_PURPOSES: Dict[int, Purpose] = {}
25+
MAPPED_SPECIAL_PURPOSES: Dict[int, MappedPurpose] = {}
26+
27+
GVL_FEATURES: Dict[int, Feature] = {}
28+
GVL_SPECIAL_FEATURES: Dict[int, Feature] = {}
29+
FEATURES_BY_NAME: Dict[str, Feature] = {}
30+
31+
MAPPED_PURPOSES_BY_DATA_USE: Dict[str, MappedPurpose] = {}
32+
33+
34+
def _load_data() -> None:
35+
with open(
36+
os.path.join(os.curdir, PURPOSE_MAPPING_FILE), encoding="utf-8"
37+
) as mapping_file:
38+
data = load(mapping_file)
39+
for raw_purpose in data["purposes"].values():
40+
purpose = Purpose.parse_obj(raw_purpose)
41+
mapped_purpose = MappedPurpose.parse_obj(raw_purpose)
42+
GVL_PURPOSES[purpose.id] = purpose
43+
MAPPED_PURPOSES[mapped_purpose.id] = mapped_purpose
44+
for data_use in mapped_purpose.data_uses:
45+
MAPPED_PURPOSES_BY_DATA_USE[data_use] = mapped_purpose
46+
47+
for raw_special_purpose in data["specialPurposes"].values():
48+
special_purpose = Purpose.parse_obj(raw_special_purpose)
49+
mapped_special_purpose = MappedPurpose.parse_obj(raw_special_purpose)
50+
GVL_SPECIAL_PURPOSES[special_purpose.id] = special_purpose
51+
MAPPED_SPECIAL_PURPOSES[mapped_special_purpose.id] = mapped_special_purpose
52+
for data_use in mapped_special_purpose.data_uses:
53+
MAPPED_PURPOSES_BY_DATA_USE[data_use] = mapped_special_purpose
54+
55+
with open(
56+
os.path.join(os.curdir, FEATURE_MAPPING_FILE), encoding="utf-8"
57+
) as feature_mapping_file:
58+
feature_data = load(feature_mapping_file)
59+
60+
for raw_feature in feature_data["features"].values():
61+
feature = Feature.parse_obj(raw_feature)
62+
GVL_FEATURES[feature.id] = feature
63+
FEATURES_BY_NAME[feature.name] = feature
64+
65+
for raw_special_feature in feature_data["specialFeatures"].values():
66+
special_feature = Feature.parse_obj(raw_special_feature)
67+
GVL_SPECIAL_FEATURES[special_feature.id] = special_feature
68+
FEATURES_BY_NAME[special_feature.name] = special_feature
69+
70+
71+
def purpose_to_data_use(purpose_id: int, special_purpose: bool = False) -> List[str]:
72+
"""
73+
Utility function to return the fideslang data uses associated with the
74+
given GVL purpose (or special purpose) ID.
75+
76+
By default, the given ID is treated as a purpose ID. The `special_purpose`
77+
argument can be set to `True` if looking up special purpose IDs.
78+
79+
Raises a KeyError if an invalid purpose ID is provided.
80+
"""
81+
purpose_map = MAPPED_SPECIAL_PURPOSES if special_purpose else MAPPED_PURPOSES
82+
return purpose_map[purpose_id].data_uses
83+
84+
85+
def data_use_to_purpose(data_use: str) -> Optional[Purpose]:
86+
"""
87+
Utility function to return the GVL purpose (or special purpose) associated
88+
with the given fideslang data use.
89+
90+
Returns None if no associated purpose (or special purpose) is found
91+
"""
92+
return MAPPED_PURPOSES_BY_DATA_USE.get(data_use, None)
93+
94+
95+
def feature_name_to_feature(feature_name: str) -> Optional[Feature]:
96+
"""Utility function to return a GVL feature (or special feature) given the feature's name"""
97+
return FEATURES_BY_NAME.get(feature_name, None)
98+
99+
100+
def feature_id_to_feature_name(
101+
feature_id: int, special_feature: bool = False
102+
) -> Optional[str]:
103+
"""Utility function to return a GVL feature/special feature name given the feature/special feature's id"""
104+
feature_map = GVL_SPECIAL_FEATURES if special_feature else GVL_FEATURES
105+
feature = feature_map.get(feature_id, None)
106+
if not feature:
107+
return None
108+
return feature.name
109+
110+
111+
_load_data()

0 commit comments

Comments
 (0)