Compare commits
10 Commits
4e5959fac4
...
grade
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
681e6b29cf | ||
| 286b05ad57 | |||
| 35225ada3c | |||
| b88f24385d | |||
| 9c5840f400 | |||
| 6e62e28be6 | |||
| caa29f2d25 | |||
| c103e8c6b4 | |||
|
|
25c999d143 | ||
|
|
b03368aa6f |
@@ -8,6 +8,4 @@ MQTT_USERNAME=
|
||||
MQTT_PASSWORD=
|
||||
REST_USERNAME=
|
||||
REST_PASSWORD=
|
||||
REST_URL=
|
||||
REST_PAGE=
|
||||
CYPRESS_TEST_URL=
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
stages:
|
||||
- gateway-build
|
||||
|
||||
- web-app-build
|
||||
- web-app-tests
|
||||
|
||||
|
||||
15
README.md
15
README.md
@@ -79,14 +79,23 @@ Credentials are available [on request](mailto:remi@heredero.ch).
|
||||
Details in [pico-sensor/README.md](pico-sensor/README.md).
|
||||
|
||||
|
||||
## Test environment
|
||||
[(Back to top)](#table-of-contents)
|
||||
|
||||
A partial test environment is deployed on the server. <br>
|
||||
Web app has a second container with the same code but made a GET request on `/test` instead of `/raclette` for the
|
||||
development environment. The gateway include a route to `/test` that return the 100 last measurement instead of the
|
||||
last 24h. Has everything else is still a development environment, the database is the same for the test and
|
||||
development environment.
|
||||
|
||||
|
||||
# Authors
|
||||
|
||||
[(Back to top)](#table-of-contents)
|
||||
|
||||
* [Sylvan Arnold](https://github.com/Sylvan22)
|
||||
* [Rémi Heredero](https://github.com/Klagarge)
|
||||
* [Yann Sierro](https://github.com/Fastium)
|
||||
* [Sylvan Arnold](https://github.com/Sylvan22) - Mainly pico-sensor part
|
||||
* [Rémi Heredero](https://github.com/Klagarge) - Mainly gateway and docker part
|
||||
* [Yann Sierro](https://github.com/Fastium) - Mainly web-app part
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ services:
|
||||
image: registry.forge.hefr.ch/team-raclette/project-softweng/gateway:latest
|
||||
container_name: gateway
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- INFLUXDB_TOKEN=$INFLUXDB_TOKEN
|
||||
- INFLUXDB_ORG=$INFLUXDB_ORG
|
||||
@@ -105,15 +103,11 @@ services:
|
||||
image: registry.forge.hefr.ch/team-raclette/project-softweng/web-app:latest
|
||||
container_name: web-app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
environment:
|
||||
- VUE_APP_REST_USER=$REST_USERNAME
|
||||
- VUE_APP_REST_PASSWORD=$REST_PASSWORD
|
||||
- VUE_APP_REST_URL=$REST_URL
|
||||
- VUE_APP_REST_PAGE=$REST_PAGE
|
||||
|
||||
- VUE_APP_REST_URL=rest.mse.kb28.ch
|
||||
- VUE_APP_REST_PAGE=raclette
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.web-app-http.entrypoints=http"
|
||||
@@ -125,3 +119,24 @@ services:
|
||||
- "traefik.http.services.web-app-https.loadbalancer.server.port=8080"
|
||||
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
web-app-test:
|
||||
image: registry.forge.hefr.ch/team-raclette/project-softweng/web-app:latest
|
||||
container_name: web-app-test
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- VUE_APP_REST_USER=$REST_USERNAME
|
||||
- VUE_APP_REST_PASSWORD=$REST_PASSWORD
|
||||
- VUE_APP_REST_URL=rest.mse.kb28.ch
|
||||
- VUE_APP_REST_PAGE=test
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.web-app-test-http.entrypoints=http"
|
||||
- "traefik.http.routers.web-app-test-http.rule=Host(`app-test.mse.kb28.ch`)"
|
||||
- "traefik.http.middlewares.web-app-test-redirect.redirectscheme.scheme=https"
|
||||
- "traefik.http.routers.web-app-test-https.entrypoints=https"
|
||||
- "traefik.http.routers.web-app-test-https.rule=Host(`app-test.mse.kb28.ch`)"
|
||||
- "traefik.http.routers.web-app-test-https.tls.certResolver=letsencrypt"
|
||||
- "traefik.http.services.web-app-test-https.loadbalancer.server.port=8080"
|
||||
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
@@ -22,6 +22,7 @@ type Gateway struct {
|
||||
mqtt paho.Client
|
||||
rest *gin.Engine
|
||||
restGroup *gin.RouterGroup
|
||||
testGroup *gin.RouterGroup
|
||||
accounts gin.Accounts
|
||||
influx influxdb2.Client
|
||||
influxApi api.WriteAPIBlocking
|
||||
@@ -100,6 +101,7 @@ func (gh *Gateway) createRestGateway() {
|
||||
|
||||
// Create a new router group with basic authentication for /raclette
|
||||
gh.restGroup = gh.rest.Group("/raclette", gin.BasicAuth(gh.accounts))
|
||||
gh.testGroup = gh.rest.Group("/test", gin.BasicAuth(gh.accounts))
|
||||
|
||||
// Define the route for the publish command
|
||||
gh.restGroup.POST("", func(c *gin.Context) {
|
||||
@@ -129,6 +131,14 @@ func (gh *Gateway) createRestGateway() {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": ret.Error()})
|
||||
}
|
||||
})
|
||||
|
||||
// Define the route for the publish command (GET)
|
||||
gh.testGroup.GET("", func(c *gin.Context) {
|
||||
ret := gh.requestInfluxTest(c)
|
||||
if ret != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": ret.Error()})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// createMQTTGateway initializes the MQTT client and sets up subscriptions.
|
||||
|
||||
@@ -68,6 +68,7 @@ func (gh *Gateway) requestInflux(c *gin.Context) error {
|
||||
|> filter(fn: (r) => r["user"] == %q)
|
||||
|> filter(fn: (r) => r["room"] == %q)
|
||||
|> filter(fn: (r) => r["device"] == %q)
|
||||
|> aggregateWindow(every: 5m, fn: mean, createEmpty: false)
|
||||
|> sort(columns: ["_time"], desc: true)
|
||||
`, INFLUXDB_BUCKET, MEASUREMENT_NAME, user, room, device)
|
||||
results, err := queryAPI.Query(context.Background(), query)
|
||||
@@ -101,3 +102,83 @@ func (gh *Gateway) requestInflux(c *gin.Context) error {
|
||||
c.JSON(http.StatusOK, values)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gh *Gateway) requestInfluxTest(c *gin.Context) error {
|
||||
// Get the user from the authenticated context
|
||||
//userId := c.MustGet(gin.AuthUserKey).(string)
|
||||
|
||||
// Get room and device from the query parameters
|
||||
user, ret := c.GetQuery("user")
|
||||
if !ret {
|
||||
return errors.New("no user found")
|
||||
}
|
||||
room, ret := c.GetQuery("room")
|
||||
if !ret {
|
||||
return errors.New(`no room found`)
|
||||
}
|
||||
device, ret := c.GetQuery("device")
|
||||
if !ret {
|
||||
return errors.New(`no device found`)
|
||||
}
|
||||
|
||||
// Get env variables and set default values if not set
|
||||
INFLUXDB_ORG, ok := os.LookupEnv("INFLUXDB_ORG")
|
||||
if !ok {
|
||||
log.Error("INFLUXDB_ORG not set, using default value: raclette")
|
||||
INFLUXDB_ORG = "raclette"
|
||||
}
|
||||
|
||||
INFLUXDB_BUCKET, ok := os.LookupEnv("INFLUXDB_BUCKET")
|
||||
if !ok {
|
||||
log.Error("INFLUXDB_BUCKET not set, using default value: raclette")
|
||||
INFLUXDB_BUCKET = "raclette"
|
||||
}
|
||||
|
||||
queryAPI := gh.influx.QueryAPI(INFLUXDB_ORG)
|
||||
MEASUREMENT_NAME, ok := os.LookupEnv("MEASUREMENT_NAME")
|
||||
if !ok {
|
||||
log.Error("MEASUREMENT_NAME not set, using default value: softweng")
|
||||
MEASUREMENT_NAME = "THC"
|
||||
}
|
||||
// The Flux query uses a large range (-1000d) and aggregates the latest values.
|
||||
// This ensures we always get the most recent data, even if the database contains old entries.
|
||||
query := fmt.Sprintf(`from(bucket: %q)
|
||||
|> range(start: -1000d)
|
||||
|> filter(fn: (r) => r["_measurement"] == %q)
|
||||
|> filter(fn: (r) => r["user"] == %q)
|
||||
|> filter(fn: (r) => r["room"] == %q)
|
||||
|> filter(fn: (r) => r["device"] == %q)
|
||||
|> sort(columns: ["_time"], desc: true)
|
||||
|> limit(n: 100)
|
||||
`, INFLUXDB_BUCKET, MEASUREMENT_NAME, user, room, device)
|
||||
results, err := queryAPI.Query(context.Background(), query)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type measurement = map[string]interface{}
|
||||
var values []measurement
|
||||
|
||||
for results.Next() {
|
||||
m := measurement{}
|
||||
record := results.Record()
|
||||
field := record.Field()
|
||||
value := record.Value()
|
||||
m["time"] = record.Time()
|
||||
m["value"] = value.(float64)
|
||||
if field == "temperature" {
|
||||
m["type"] = "temperature"
|
||||
}
|
||||
if field == "humidity" {
|
||||
m["type"] = "humidity"
|
||||
}
|
||||
values = append(values, m)
|
||||
fmt.Println(results.Record())
|
||||
}
|
||||
if err := results.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, values)
|
||||
return nil
|
||||
}
|
||||
|
||||
72
management_web_part_group_4.md
Normal file
72
management_web_part_group_4.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Group 4 - project management and web part feedback
|
||||
|
||||
## Gitlab board and project management
|
||||
### Requirements
|
||||
1. Check the project description and split project requirements into gitlab iterations or gitlab milestones.
|
||||
2. Define a couple of users stories following the example and structure provided in the course slides (put them in the gitlab board backlog at this stage) as issues in gitlab board.
|
||||
3. Assign every story to one of your gitlab iterations (or gitlab milestones).
|
||||
4. Split user stories into tasks (add the tasks to the gitlab board descriptions.
|
||||
5. Estimate 2 user stories tasks using poker cards. Timeless story point estimations are encouraged. Nevertheless, and as agreed in class, If your group wishes to proceed with a time-based estimation then it's OK (for as long as at least one user story is also estimated in terms of timeless story points (per task and added together for the user story and the result should be put in its description). Add story points estimation per task and user story to the corresponding issue description (for 1 to 2 user stories only).
|
||||
6. Discuss differences (and document the process: duration, points of disagreements, adjustments, clarifications, perceived exercice usefulness). This documentation is added as a file in your gitlab repo (under the name: poker_planning_retro.md).
|
||||
7. Once team reaches agreement on task estimation, sum up all tasks points and add them to the chosen user story “weight” on gitlab.
|
||||
8. Use your gitlab board during the semester in a consistent way to track issue progress. At the end of the semester, the board has to be up to date with development (e.g. close finished tasks, etc).
|
||||
|
||||
### Grade feedback : 3.5/3.5
|
||||
* Pokercard planning: All won't change there vote, they are convice it's this level of difficulty. => amusing to some extent! but usually you are expected to reach a consensus and bring convincing facts to reach it.
|
||||
* Thank you for the precise reporting and clearly mentioning your metric.
|
||||
* Very good usage of the gitlab boards with labels
|
||||
* Convention commits : ok (well done)
|
||||
* Links between issues and commits: ok
|
||||
|
||||
|
||||
|
||||
|
||||
## Design
|
||||
### Requirements
|
||||
In this part, you are expected to deliver a documentation of your projet's design specifications, with as much as possible reference to what we have seen in this lecture. Those of you who attended this lecture, were shown sample documentations that meet our expectations. Your documentation should typically specify/include:
|
||||
* Which specific architecture styles and architecture and design patterns you will be using and make sure you refer to them correctly (a must have). Use the course slides as your reference.
|
||||
* The C4 model and/or the Kruchten's 4+ 1 views, to represent your design (strongly encouraged).
|
||||
* a couple of other complementary diagrams are also expected to illustrate/represent your projet's global architecture, how systems components will communicate, your technology stack (which will still evolve as we progress throughout the course), deployment diagrams (Kruchten's 4+1 views can typically be useful for this matter). Use UML only if you are confortable with it, otherwise it is not imposed.
|
||||
* It's about the quality not the quantity; aim for a clear, concise, and precise documentation.
|
||||
* Once your design is specified, take some time to update your gitlab boards, prepare user stories (and corresponding tasks).
|
||||
* As you design your components and your code, keep also in mind composition (interfaces vs inheritance), and aspects related to design by contract.
|
||||
* Feel free to send me a draft until March 4, to receive a formative (non graded) feedback, to make sure sure your submissions meets expectations.
|
||||
* Your first documentation draft and the final one must be in your gitlab repository in the main branch, in a folder named "design" (in pdf or mark down format).
|
||||
|
||||
### Grade feedback : 2/3.5
|
||||
* Very good contexte and container diagrams
|
||||
* Class diagram ok too but couldn’t find a component diagram
|
||||
* Plus I didn’t find any information regarding architecture styles and patterns identified and used
|
||||
|
||||
|
||||
## Dockerized app
|
||||
### Requirements
|
||||
Your project must be fully dockerized.
|
||||
* Define a docker image per main component.
|
||||
* Then use docker-compose to run services from your docker images. Propose a complete solution for services that depend on one another.
|
||||
* Volumes or other types of storage drivers might also be useful. The system must be easy to launch locally with simple "docker-compose up command".
|
||||
* Your dockerized system should be run on gitlab (which might raise some challenge, when it comes to automatic deployment, demos/hints will be given in subsequent lectures).
|
||||
|
||||
### Grade feedback : /3
|
||||
*
|
||||
|
||||
## Web app Testing
|
||||
### Requirements
|
||||
While it should be clear to all of us, that test coverage is important in real working environments, in this project specifically, the focus is not on coverage but on be able to differentiate different types of tests and experiment with different testing technologies. So each group is expect to:
|
||||
* Implement 2 Unit tests frontend (using cypress and with mock data).
|
||||
* Implement 1 Integration test frontend/backend ou a full E2E (end-to-end) test for a key feature: for example getting a specific measurements saved in the backend (in files our case) and to be displayed on the frontend Web page.
|
||||
* Tests should be called at every commit; a CI/CD pipeline needs to be using gitlab.yml (and relying on provided examples).
|
||||
* Test cases must be documented in a markdown file named web-tests.md, clarifying for each tests: the 1) test purpose, 2) the test type (unit, integration, E2E) as well as 3) the type of testing double chosen to conduct the test.
|
||||
|
||||
### Grade feedback : /4
|
||||
*
|
||||
|
||||
## Webapp CI/CD
|
||||
### Requirements
|
||||
* Build a CI/CD pipeline for your dockerized project, especially in relation to deploying the required image on gitlab and using docker-in-docker
|
||||
* Tests should be called at every commit; a CI/CD pipeline needs to be using gitlab.yml
|
||||
|
||||
### Grade feedback : /3
|
||||
|
||||
|
||||
Total Project Grade (with the Embedded Part) over 24: 19.5
|
||||
@@ -70,7 +70,7 @@ RUN \
|
||||
ENV PICO_SDK_PATH=/apps/pico-sdk/
|
||||
|
||||
# Patch the SDK for semihosting file I/O (needed for gcov)
|
||||
COPY newlib_interface.c.patched ${PICO_SDK_PATH}/src/rp2_common/pico_clib_interface/newlib_interface.c
|
||||
COPY ./pico-sensor/newlib_interface.c.patched ${PICO_SDK_PATH}/src/rp2_common/pico_clib_interface/newlib_interface.c
|
||||
|
||||
# Build picotool so we don't have to build it for the project
|
||||
RUN \
|
||||
@@ -65,7 +65,10 @@ npm run build
|
||||
|
||||
<p align="center"> 
|
||||
|
||||
### Links
|
||||
## Tests
|
||||
Details in [web-app/test.md](web-app/test.md)
|
||||
|
||||
# Links
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).\
|
||||
See [Vue.js](https://vuejs.org/guide/introduction.html).\
|
||||
See [chartjs](https://www.chartjs.org/docs/latest/).\
|
||||
|
||||
@@ -65,9 +65,6 @@ npx cypress open
|
||||
- Tests fetching timeseries data with a valid user-room-device selection, ensuring the main chart is displayed and no "no data" message appears.
|
||||
- Tests fetching timeseries data with an invalid user-room-device selection (e.g., changing the user to "Sylvan"), ensuring the main chart is not displayed and the "no data" message is shown.
|
||||
|
||||
#### Important Notes
|
||||
The E2E tests use a web-app development image deployed on https://app.mse.kb28.ch/ for the moment. Because there is a problem with the service image in the CI. The environment variables cannot be set correctly to the job's service. An issue is opened to fix this problem: [#37].
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- **Serie Component**:
|
||||
@@ -76,3 +73,9 @@ The E2E tests use a web-app development image deployed on https://app.mse.kb28.c
|
||||
- Ensures the label "Unknown" is returned for unknown types.
|
||||
- Verifies that temperature and humidity data are formatted correctly by `getSerie()`, including label, color, and data structure.
|
||||
- Ensures unknown types are handled appropriately in `getSerie()`, defaulting to the correct color and axis.
|
||||
|
||||
## Test in CI
|
||||
|
||||
All test are covered by the CI pipeline.
|
||||
The unit tests are run on every commit and doesn't require any specific resources.
|
||||
The E2E tests are run too on every commit, but they require a test URL to be set in the environment variable `CYPRESS_TEST_URL` which provide an access to a test instance of the web-app. Actually, these tests are running on the test development instance at `https://app-dev.mse.kb28.ch/`.
|
||||
|
||||
Reference in New Issue
Block a user