Skip to content

Commit f29d72c

Browse files
authored
Merge pull request #1 from miriamkw/repo_cleanup
Repo cleanup
2 parents da34ae4 + 6bcd899 commit f29d72c

19 files changed

Lines changed: 123072 additions & 68 deletions

File tree

.github/workflows/ci.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: CI Workflow
2+
3+
# Trigger the workflow on push and pull request events
4+
on:
5+
push:
6+
pull_request:
7+
8+
# Define the jobs to run in the workflow
9+
jobs:
10+
build-and-test:
11+
runs-on: macos-latest # Use macOS for Swift and iOS-specific dependencies
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v3
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.9' # Specify your required Python version
21+
22+
- name: Set up Swift
23+
run: |
24+
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
25+
sudo xcodebuild -runFirstLaunch
26+
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
31+
32+
- name: Run build script
33+
run: |
34+
chmod +x build.sh
35+
./build.sh
36+
37+
- name: Run tests
38+
run: |
39+
pytest
40+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ DerivedData/
66
.swiftpm/configuration/registries.json
77
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
88
.netrc
9+
.idea

Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import PackageDescription
55

66
let package = Package(
77
name: "LoopAlgorithmToPython",
8+
defaultLocalization: "no",
89
platforms: [
910
.macOS(.v13),
1011
.iOS(.v15),
@@ -30,6 +31,9 @@ let package = Package(
3031
),
3132
.testTarget(
3233
name: "LoopAlgorithmToPythonTests",
33-
dependencies: ["LoopAlgorithmToPython"]),
34+
dependencies: ["LoopAlgorithmToPython"],
35+
resources: [
36+
.process("TestData")
37+
])
3438
]
3539
)

README.md

Lines changed: 161 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,101 +2,200 @@
22

33
This Swift module uses LoopAlgorithm to create C functions for generating predictions and prediction dates from JSON data.
44

5-
How?
6-
I create a foreign function interface (FFI) in Swift by using the unofficial @_cdecl Swift function. This interfaces the Swift code with C. Then we can create a dynamic library, import it into a Python (or other) repositories, and use for example ctypes to compile the C code.
5+
This is achieved by creating a foreign function interface (FFI) in Swift by using the unofficial @_cdecl Swift function. This interfaces the Swift code with C. Then we can create a dynamic library, import it into a Python (or other) repositories, and use for example ctypes to compile the C code.
76

87

9-
## Installation
108

11-
1. Clone the repository
12-
2. Build the dynamic library:
9+
## Repository Overview
1310

14-
```
15-
swift package clean
16-
swift build --configuration release
17-
```
18-
Check if the dynamic library got properly generated and print the path:
19-
```
20-
find .build -name "libLoopAlgorithmToPython.dylib"
21-
```
22-
Output should be something like: /release/libLoopAlgorithmToPython.dylib
11+
### Exposed functions
2312

24-
Copy that file into your repository.
13+
You can find the C-exposed functions in the file `Sources/LoopAlgorithmToPython/LoopAlgorithmToPython.swift`.
2514

15+
### Python API
2616

27-
## Exposed functions
17+
Python API functions are located in `loop_to_python_api/api.py`.
2818

29-
You can find the C-exposed functions in the file `LoopAlgorithmToPython.swift`.
19+
### Tests and test data
3020

21+
`python_tests/` contains examples of executing all the functions as well as example files providing templates on how to structure the input files.
3122

3223

33-
## Usage in Python
3424

35-
Here's how you can use the dynamic library (`libLoopAlgorithmToPython.dylib`) in Python to call the exposed functions:
3625

37-
```
38-
import ctypes
39-
import json
26+
## Python API Functions
4027

41-
json_file_path = 'some_file.json'
28+
-------------------------
4229

43-
# Load the shared library
44-
swift_lib = ctypes.CDLL('./libLoopAlgorithmToPython.dylib')
30+
### Initialize Exception Handlers
4531

46-
# Specify the argument types and return type of the Swift function
47-
swift_lib.generatePrediction.argtypes = [ctypes.c_char_p]
48-
swift_lib.generatePrediction.restype = ctypes.POINTER(ctypes.c_double)
32+
`initialize_exception_handlers()`
4933

50-
swift_lib.getPredictionDates.argtypes = [ctypes.c_char_p]
51-
swift_lib.getPredictionDates.restype = ctypes.c_char_p
34+
Initializes the exception and signal handlers in the Swift library to provide more informative error messages.
5235

53-
swift_lib.getActiveCarbs.argtypes = [ctypes.c_char_p]
54-
swift_lib.getActiveCarbs.restype = ctypes.c_double
36+
-------------------------
5537

56-
swift_lib.getActiveInsulin.argtypes = [ctypes.c_char_p]
57-
swift_lib.getActiveInsulin.restype = ctypes.c_double
38+
### Generate Prediction
5839

59-
# Read JSON file
60-
def read_json_file(file_path):
61-
with open(file_path, 'r') as f:
62-
data = json.load(f)
63-
return data
40+
`generate_prediction(json_file, len=72)`
6441

65-
json_data = read_json_file(json_file_path) # Read JSON file
66-
json_str = json.dumps(json_data) # Convert JSON data to JSON string
67-
json_bytes = json_str.encode('utf-8') # Convert JSON string to bytes
42+
Generates a prediction based on the provided JSON input.
6843

69-
# Prepare a variable to receive the length of the predicted values
70-
length = 82
44+
- **Parameters**:
45+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
46+
- `len` (optional): The number of prediction values to generate. Defaults to 72.
47+
- **Returns**: A list of prediction values.
7148

72-
# Call the Swift functions
73-
result_prediction_values = swift_lib.generatePrediction(json_bytes)
74-
result_prediction_dates = swift_lib.getPredictionDates(json_bytes).decode('utf-8')
75-
result_active_carbs = swift_lib.getActiveCarbs(json_bytes)
76-
result_active_insulin = swift_lib.getActiveInsulin(json_bytes)
49+
-------------------------
7750

78-
# Read the generated predictions
79-
array = [result_prediction_values[i] for i in range(length)]
80-
print(array[0])
81-
print(f"The result from generatePrediction is: {array}")
51+
### Get Prediction Dates
8252

83-
# Read the dates
84-
date_list = result.split(',')[:-1]
85-
print(f"The result from getPredictionDates is: {date_list}")
53+
`get_prediction_dates(json_file)`
8654

87-
# Read the active carbohydrates
88-
print(f"The result from getActiveCarbs is: {result_active_carbs}")
55+
Fetches prediction dates based on the provided JSON input.
8956

90-
# Read the active insulin
91-
print(f"The result from getActiveInsulin is: {result_active_insulin}")
92-
```
57+
- **Parameters**:
58+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
59+
- **Returns**: A list of prediction dates as strings.
9360

94-
Adjust the paths, function names, and details as per your specific project setup and requirements.
61+
-------------------------
9562

63+
### Get Prediction Values and Dates
9664

65+
`get_prediction_values_and_dates(json_file)`
9766

67+
Combines the `generate_prediction` and `get_prediction_dates` functions to return both prediction values and dates.
9868

69+
- **Parameters**:
70+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
71+
- **Returns**: A tuple containing a list of prediction values and a list of prediction dates.
9972

73+
-------------------------
74+
75+
### Get Glucose Effect Velocity
76+
77+
`get_glucose_effect_velocity(json_file, len=72)`
78+
79+
Fetches the glucose effect velocity, which is equivalent to Insulin Counteraction Effect (ICE).
80+
81+
- **Parameters**:
82+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
83+
- `len` (optional): The number of values to fetch. Defaults to 72.
84+
- **Returns**: A list of glucose effect velocity values.
85+
86+
-------------------------
87+
88+
### Get Glucose Effect Velocity Dates
89+
90+
`get_glucose_effect_velocity_dates(json_file)`
91+
92+
Fetches the dates associated with the glucose effect velocity.
93+
94+
- **Parameters**:
95+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
96+
- **Returns**: A list of dates as strings.
97+
98+
99+
-------------------------
100+
101+
### Get Glucose Velocity Values and Dates
102+
103+
`get_glucose_velocity_values_and_dates(json_file)`
104+
105+
Combines the `get_glucose_effect_velocity` and `get_glucose_effect_velocity_dates` functions to return both glucose effect velocity values and dates.
106+
107+
- **Parameters**:
108+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
109+
- **Returns**: A tuple containing a list of glucose effect velocity values and a list of dates.
110+
111+
-------------------------
112+
113+
### Get Active Carbs
114+
115+
`get_active_carbs(json_file)`
116+
117+
Fetches the active carbohydrates based on the provided JSON input.
118+
119+
- **Parameters**:
120+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
121+
- **Returns**: The active carbohydrates as a double.
122+
123+
124+
-------------------------
125+
126+
### Get Active Insulin
127+
128+
`get_active_insulin(json_file)`
129+
130+
Fetches the active insulin based on the provided JSON input.
131+
132+
- **Parameters**:
133+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
134+
- **Returns**: The active insulin as a double.
135+
136+
-------------------------
137+
138+
### Percent Absorption at Percent Time
139+
140+
`percent_absorption_at_percent_time(percent_time)`
141+
142+
Calculates the percentage of carbohydrate absorption at a given percent time using a piecewise linear model.
143+
144+
- **Parameters**:
145+
- `percent_time`: The time as a fraction (e.g., 0.2 for 20%).
146+
- **Returns**: The percentage of absorption as a double.
147+
148+
-------------------------
149+
150+
### Piecewise Linear Percent Rate at Percent Time
151+
152+
`piecewise_linear_percent_rate_at_percent_time(percent_time)`
153+
154+
Calculates the percentage rate of carbohydrate absorption at a given percent time using a piecewise linear model.
155+
156+
- **Parameters**:
157+
- `percent_time`: The time as a fraction (e.g., 0.2 for 20%).
158+
- **Returns**: The percentage rate of absorption as a double.
159+
160+
-------------------------
161+
162+
### Linear Percent Rate at Percent Time
163+
164+
`linear_percent_rate_at_percent_time(percent_time)`
165+
166+
Calculates the percentage rate of carbohydrate absorption at a given percent time using a linear model.
167+
168+
- **Parameters**:
169+
- `percent_time`: The time as a fraction (e.g., 0.2 for 20%).
170+
- **Returns**: The percentage rate of absorption as a double.
171+
172+
-------------------------
173+
174+
### Get Dynamic Carbs on Board
175+
176+
`get_dynamic_carbs_on_board(json_file)`
177+
Fetches the dynamic carbohydrates on board based on the provided JSON input.
178+
179+
- **Parameters**:
180+
- `json_file`: The JSON data input. See python tests and test files for example inputs.
181+
- **Returns**: The dynamic carbohydrates on board as a double.
182+
183+
-------------------------
184+
185+
186+
187+
188+
## Build Dynamic Library
189+
190+
The file `python_api/libLoopAlgorithmToPython.dylib` contains the dynamic library that is containing the C-embedded Swift functions.
191+
192+
After making changes in the Swift code, rebuild the dynamic library by running `chmod +x build.sh` followed by `./build.sh`.
193+
194+
195+
196+
## Run Tests
197+
198+
Run command `pytest`.
100199

101200

102201

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import class Foundation.Bundle
2+
import class Foundation.ProcessInfo
3+
import struct Foundation.URL
4+
5+
private class BundleFinder {}
6+
7+
extension Foundation.Bundle {
8+
/// Returns the resource bundle associated with the current Swift module.
9+
static let module: Bundle = {
10+
let bundleName = "LoopAlgorithmToPython_LoopAlgorithmToPythonTests"
11+
12+
let overrides: [URL]
13+
#if DEBUG
14+
// The 'PACKAGE_RESOURCE_BUNDLE_PATH' name is preferred since the expected value is a path. The
15+
// check for 'PACKAGE_RESOURCE_BUNDLE_URL' will be removed when all clients have switched over.
16+
// This removal is tracked by rdar://107766372.
17+
if let override = ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_PATH"]
18+
?? ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_URL"] {
19+
overrides = [URL(fileURLWithPath: override)]
20+
} else {
21+
overrides = []
22+
}
23+
#else
24+
overrides = []
25+
#endif
26+
27+
print("BUNDLEFILE")
28+
29+
let candidates = overrides + [
30+
// Bundle should be present here when the package is linked into an App.
31+
Bundle.main.resourceURL,
32+
33+
// Bundle should be present here when the package is linked into a framework.
34+
Bundle(for: BundleFinder.self).resourceURL,
35+
36+
// For command-line tools.
37+
Bundle.main.bundleURL,
38+
]
39+
40+
for candidate in candidates {
41+
let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
42+
print(bundlePath)
43+
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
44+
print("BUNDLE:", bundle)
45+
return bundle
46+
}
47+
}
48+
fatalError("unable to find bundle named LoopAlgorithmToPython_LoopAlgorithmToPythonTests")
49+
}()
50+
}

0 commit comments

Comments
 (0)