Skip to content

Commit df3039f

Browse files
mirkoCrobumirkoCrobualessio-perugini
authored
Refactor: relocate assets from mbed to FileSystem (#541)
Co-authored-by: mirkoCrobu <mirkocrobu@NB-0531.localdomain> Co-authored-by: Alessio Perugini <alessio@perugini.xyz>
1 parent cf04760 commit df3039f

File tree

135 files changed

+5431
-365
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+5431
-365
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ build/
3434
/arduino-app-cli
3535
/apps
3636
# temporary staging directories
37-
/debian/arduino-app-cli/home/
37+
/debian/arduino-app-cli/home/arduino/.local/share/arduino-app-cli/examples

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ The orchestrator will write apps in `~/ArduinoApps` folder, and examples in `~/.
252252
To override the path provide the following flags:
253253

254254
- `ARDUINO_APP_CLI__APPS_DIR`
255-
- `ARDUINO_APP_CLI__DATA_DIR`
255+
- `ARDUINO_APP_CLI__CONFIG_DIR`
256256

257257
### App folder and persistent data
258258

Taskfile.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@ tasks:
107107
set -e
108108
echo "Runner version set as: {{ .EXAMPLE_VERSION }}"
109109
TMP_PATH="$(mktemp -d)"
110-
DEST_PATH="debian/arduino-app-cli/home/arduino/.config/arduino-app-cli/"
110+
DEST_PATH="debian/arduino-app-cli/home/arduino/.local/share/arduino-app-cli/"
111111
echo "Cloning bcmi-labs/app-bricks-example into temporary directory ${TMP_PATH}..."
112112
git clone --depth 1 --branch "{{ .EXAMPLE_VERSION }}" https://github.com/bcmi-labs/app-bricks-example "${TMP_PATH}"
113-
rm -rf "${DEST_PATH}"
113+
rm -rf "${DEST_PATH}/examples"
114114
mkdir -p "${DEST_PATH}"
115115
mv "${TMP_PATH}/examples" "${DEST_PATH}"
116-
rm -rf "${TMP_PATH}"
116+
rm -rf "${TMP_PATH}/examples"
117117
echo "Examples successfully cloned."
118118
silent: false
119119

@@ -333,7 +333,8 @@ tasks:
333333
- |
334334
tmpdir="${TMPDIR:-/tmp}"
335335
semver_tag={{.RUNNER_VERSION}}
336-
DST_DIR=internal/orchestrator/assets/static/"${semver_tag}"
336+
ROOT_DEST_DIR=debian/arduino-app-cli/home/arduino/.local/share/arduino-app-cli/assets
337+
DST_DIR="${ROOT_DEST_DIR}/${semver_tag}"
337338
if [ -d "$DST_DIR" ]; then
338339
exit 0
339340
fi
@@ -342,8 +343,8 @@ tasks:
342343
ZIP_NAME="$tmpdir"/arduino_app_bricks-"${semver_tag}"-py3-none-any.whl
343344
OUTPUT_DIR="${tmpdir}/${semver_tag}"
344345
unzip -o "$ZIP_NAME" -d "$OUTPUT_DIR"
345-
rm -rf "internal/orchestrator/assets/static"
346-
mkdir -p "internal/orchestrator/assets/static"
346+
rm -rf "$ROOT_DEST_DIR"
347+
mkdir -p "$ROOT_DEST_DIR"
347348
mv "$OUTPUT_DIR"/arduino/app_bricks/static "$DST_DIR"
348349
rm -r "$OUTPUT_DIR" "$ZIP_NAME"
349350
# Bumps the default pinned latest base python image tag

cmd/arduino-app-cli/app/start.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func startHandler(ctx context.Context, cfg config.Configuration, app app.Arduino
4444
servicelocator.GetBricksIndex(),
4545
app,
4646
cfg,
47+
servicelocator.GetStaticStore(),
4748
)
4849
for message := range stream {
4950
switch message.GetType() {

cmd/arduino-app-cli/daemon/daemon.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func NewDaemonCmd(cfg config.Configuration, version string) *cobra.Command {
5555
servicelocator.GetBricksIndex(),
5656
servicelocator.GetAppIDProvider(),
5757
cfg,
58+
servicelocator.GetStaticStore(),
5859
)
5960
if err != nil {
6061
slog.Error("Failed to start default app", slog.String("error", err.Error()))

cmd/arduino-app-cli/internal/servicelocator/servicelocator.go

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,18 @@ var (
3535
runnerVersion = "0.1.16"
3636

3737
GetBricksIndex = sync.OnceValue(func() *bricksindex.BricksIndex {
38-
var bIndex *bricksindex.BricksIndex
39-
if GetProvisioner().IsUsingDynamicProvision() {
40-
dynamicProvisionDir := GetProvisioner().DynamicProvisionDir()
41-
bIndex = f.Must(bricksindex.GenerateBricksIndexFromFile(dynamicProvisionDir))
42-
} else {
43-
bricksIndexContent := f.Must(GetStaticStore().GetBricksListFile())
44-
defer bricksIndexContent.Close()
45-
bIndex = f.Must(bricksindex.LoadBricksIndex(bricksIndexContent))
46-
}
47-
return bIndex
38+
return f.Must(bricksindex.GenerateBricksIndexFromFile(GetStaticStore().GetAssetsFolder()))
4839
})
4940

5041
GetModelsIndex = sync.OnceValue(func() *modelsindex.ModelsIndex {
51-
var mIndex *modelsindex.ModelsIndex
52-
if GetProvisioner().IsUsingDynamicProvision() {
53-
dynamicProvisionDir := GetProvisioner().DynamicProvisionDir()
54-
mIndex = f.Must(modelsindex.GenerateModelsIndexFromFile(dynamicProvisionDir))
55-
} else {
56-
modelsIndexContent := f.Must(GetStaticStore().GetModelsListFile())
57-
defer modelsIndexContent.Close()
58-
mIndex = f.Must(modelsindex.LoadModelsIndex(modelsIndexContent))
59-
}
60-
return mIndex
42+
return f.Must(modelsindex.GenerateModelsIndexFromFile(GetStaticStore().GetAssetsFolder()))
6143
})
6244

6345
GetProvisioner = sync.OnceValue(func() *orchestrator.Provision {
64-
pythonImage, usedPythonImageTag := getPythonImageAndTag()
65-
slog.Debug("Using pythonImage", slog.String("image", pythonImage))
66-
46+
pythonImage, _ := getPythonImageAndTag()
6747
return f.Must(orchestrator.NewProvision(
6848
GetDockerClient(),
69-
GetStaticStore(),
70-
usedPythonImageTag != runnerVersion,
49+
shouldUseDynamicProvisioning(),
7150
pythonImage,
7251
))
7352
})
@@ -102,7 +81,13 @@ var (
10281
})
10382

10483
GetStaticStore = sync.OnceValue(func() *store.StaticStore {
105-
return store.NewStaticStore(runnerVersion)
84+
var baseDir string
85+
if GetProvisioner().IsUsingDynamicProvision() {
86+
baseDir = GetProvisioner().DynamicProvisionDir().String()
87+
} else {
88+
baseDir = globalConfig.AssetsDir().Join(runnerVersion).String()
89+
}
90+
return store.NewStaticStore(baseDir)
10691
})
10792

10893
GetBrickService = sync.OnceValue(func() *bricks.Service {
@@ -136,3 +121,9 @@ func getPythonImageAndTag() (string, string) {
136121
}
137122
return pythonImage, usedPythonImageTag
138123
}
124+
125+
func shouldUseDynamicProvisioning() bool {
126+
pythonImage, usedPythonImageTag := getPythonImageAndTag()
127+
slog.Debug("Using pythonImage", slog.String("image", pythonImage))
128+
return usedPythonImageTag != runnerVersion
129+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/bin/sh
22

3-
chown -R arduino:arduino /home/arduino/.config/arduino-app-cli/examples
3+
chown -R arduino:arduino /home/arduino/.config/arduino-app-cli
4+
chown -R arduino:arduino /home/arduino/.local/share/arduino-app-cli
45

56
systemctl enable arduino-app-cli
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# air_quality_monitoring API Reference
2+
3+
## Index
4+
5+
- Class `AQILevel`
6+
- Class `AirQualityData`
7+
- Class `AirQualityMonitoring`
8+
- Class `AirQualityLookupError`
9+
10+
---
11+
12+
## `AQILevel` dataclass
13+
14+
```python
15+
class AQILevel()
16+
```
17+
18+
Data class to represent AQI levels.
19+
20+
### Attributes
21+
22+
- **min_value** (*int*): Minimum AQI value for the level.
23+
- **max_value** (*int*): Maximum AQI value for the level.
24+
- **description** (*str*): Description of the AQI level.
25+
- **color** (*str*): Color associated with the AQI level in hex.
26+
27+
28+
---
29+
30+
## `AirQualityData` dataclass
31+
32+
```python
33+
class AirQualityData()
34+
```
35+
36+
Data class to represent air quality data.
37+
38+
### Attributes
39+
40+
- **city** (*str*): Name of the city.
41+
- **lat** (*float*): Latitude of the city.
42+
- **lon** (*float*): Longitude of the city.
43+
- **url** (*str*): URL for more information about the air quality data.
44+
- **last_update** (*str*): Last update timestamp of the air quality data.
45+
- **aqi** (*int*): Air Quality Index value.
46+
- **dominantpol** (*str*): Dominant pollutant in the air.
47+
- **iaqi** (*dict*): Individual AQI values for various pollutants.
48+
49+
### Methods
50+
51+
#### `AirQualityData.pandas_dict()`
52+
53+
Return the data as a dictionary suitable for pandas DataFrame.
54+
55+
56+
---
57+
58+
## `AirQualityMonitoring` class
59+
60+
```python
61+
class AirQualityMonitoring(token: str)
62+
```
63+
64+
Class to get air quality data from AQICN API.
65+
66+
### Parameters
67+
68+
- **token** (*str*): API token for AQICN service.
69+
70+
### Raises
71+
72+
- **ValueError**: If the token is not provided.
73+
74+
### Methods
75+
76+
#### `AirQualityMonitoring.get_air_quality_by_city(city: str)`
77+
78+
Get air quality data by city name.
79+
80+
##### Parameters
81+
82+
- **city** (*str*): Name of the city.
83+
84+
##### Returns
85+
86+
- (*AirQualityData*): Air quality assembled data.
87+
88+
##### Raises
89+
90+
- **AirQualityLookupError**: If the API request fails.
91+
92+
#### `AirQualityMonitoring.get_air_quality_by_coords(latitude: float, longitude: float)`
93+
94+
Get air quality data by coordinates.
95+
96+
##### Parameters
97+
98+
- **latitude** (*float*): Latitude.
99+
- **longitude** (*float*): Longitude.
100+
101+
##### Returns
102+
103+
- (*AirQualityData*): Air quality assembled data.
104+
105+
##### Raises
106+
107+
- **AirQualityLookupError**: If the API request fails.
108+
109+
#### `AirQualityMonitoring.get_air_quality_by_ip()`
110+
111+
Get air quality data by IP address.
112+
113+
##### Returns
114+
115+
- (*AirQualityData*): Air quality assembled data.
116+
117+
##### Raises
118+
119+
- **AirQualityLookupError**: If the API request fails.
120+
121+
#### `AirQualityMonitoring.process(item: dict)`
122+
123+
Process the input dictionary to get air quality data.
124+
125+
##### Parameters
126+
127+
- **item** (*dict*): Input dictionary containing either 'city', 'latitude' and 'longitude', or 'ip'.
128+
129+
##### Returns
130+
131+
- (*dict*): Air quality data.
132+
133+
##### Raises
134+
135+
- **ValueError**: If the input dictionary is not valid.
136+
137+
#### `AirQualityMonitoring.assemble_data(data: dict)`
138+
139+
Create a payload for the air quality data.
140+
141+
##### Parameters
142+
143+
- **data** (*dict*): Air quality data.
144+
145+
##### Returns
146+
147+
- (*dict*): Payload with relevant air quality information.
148+
149+
#### `AirQualityMonitoring.map_aqi_level(aqi: int)`
150+
151+
Returns AQILevel class matching provided AQI.
152+
153+
154+
---
155+
156+
## `AirQualityLookupError` class
157+
158+
```python
159+
class AirQualityLookupError(message: str, status: str)
160+
```
161+
162+
Custom exception for air quality lookup errors.
163+
164+
### Parameters
165+
166+
- **message** (*str*): Error message.
167+
- **status** (*str*): Status of the error, defaults to None.
168+
169+
### Methods
170+
171+
#### `AirQualityLookupError.from_api_response(cls, data: dict)`
172+
173+
AirQualityLookupError error handling based on response provided by AQI API.
174+
175+
Documented errors:
176+
- {"status": "error", "data": "Invalid key"}
177+
- {"status": "error", "data": "Unknown station"}
178+
- {"status": "error", "data": "Over quota"}
179+
- {"status": "error", "data": "Invalid query"}
180+
- {"status": "error", "data": "Too Many Requests"}
181+
- {"status": "error", "data": "IP not allowed"}
182+
- {"status": "error", "data": "Unknown error"}
183+
- {"status": "error", "data": {"message": "..."}}
184+
185+
##### Parameters
186+
187+
- **data** (*dict*): Response data from the AQI API.
188+
189+
##### Returns
190+
191+
- (*AirQualityLookupError*): An instance of AirQualityLookupError with the error message and status.
192+

0 commit comments

Comments
 (0)