diff --git a/README2.md b/README2.md new file mode 100644 index 0000000..89606e1 --- /dev/null +++ b/README2.md @@ -0,0 +1,105 @@ +# oraclequantapi- Measurement Parser API + +A Spring Boot REST API that parses encoded measurement strings and stores the history in an Oracle XE database. + +--- + +## How to Run + +1. Start Oracle XE: + +run the docker start oracle-xe + + +2. Build the JAR: +----- +./mvnw clean package +----- +3. Run the app: +------ +java -jar target/oraclequantapi-0.0.1.jar +------ +4. Open the browser and test: +------ +http://localhost:8080/convert-measurements?input=abbcc +------ + +...................... + +## How to Deploy to Oracle Linux VM + +1. Copy the JAR to the VM: +------- +scp target/oraclequantapi-0.0.1 safa@:~/ +------- + +2. SSH into the VM: +------- +ssh safa@ +------- + +3. Run the app: +------- +java -jar ~/tryout-0.0.1-SNAPSHOT.jar +------- + +4. Test from your browser: +------- +http://:8080/convert-measurements?input=abbcc +------- + +....................... + +## How to Configure the Database + +Open `src/main/resources/application.properties` and update these values: + +```properties +spring.datasource.url=jdbc:oracle:thin:@:1521/XEPDB1 +spring.datasource.username=system +spring.datasource.password= +``` + +- Replace `` with `localhost` if running locally, or your Mac IP if running from the VM +- Replace `` with the password you set when creating the Oracle XE container + +......................... + +## API Endpoints + +| GET | `/convert-measurements?input=` | Parse input and return result | +| GET | `/history` | Get all history records | +| GET | `/history/{id}` | Get one record by ID | +| PUT | `/history/{id}` | Update a record by ID | +| DELETE | `/history` | Clear all history records| + +### Examples + +**Convert:** +``` +GET http://localhost:8080/convert-measurements?input=abbcc +Response: [2, 6] +``` + +**Get all history:** +``` +GET http://localhost:8080/history +``` + +**Get one record:** +``` +GET http://localhost:8080/history/1 +``` + +**Update a record:** +``` +PUT http://localhost:8080/history/1 +Body: { "input": "abbcc" } +``` + +**Clear history:** +``` +DELETE http://localhost:8080/history +Response: History has been Deleted +``` + diff --git a/pom.xml b/pom.xml index 20909d2..070b260 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.oraclequantapi oraclequantapi - 0.0.1-SNAPSHOT + 0.0.1 @@ -40,6 +40,17 @@ spring-boot-starter-test test + + + com.oracle.database.jdbc + ojdbc11 + runtime + + + + org.springframework.boot + spring-boot-starter-data-jpa + diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/controller/HistoryController.java b/src/main/java/com/oraclequantapi/oraclequantapi/controller/HistoryController.java new file mode 100644 index 0000000..4e9e255 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/controller/HistoryController.java @@ -0,0 +1,45 @@ +package com.oraclequantapi.oraclequantapi.controller; +import com.oraclequantapi.oraclequantapi.model.MeasurementRecord; +import com.oraclequantapi.oraclequantapi.service.HistoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.List; +import java.util.Map; + +@RestController +// All endpoints in this class start with "/history" +@RequestMapping(path = "/history") +public class HistoryController { + + @Autowired + public HistoryService historyService; +//Returns all history records from the database as a JSON array + @GetMapping + public ResponseEntity> getAll() { + return ResponseEntity.status(HttpStatus.OK).body(historyService.getAll()); + } +//Returns a specific record by its ID + @GetMapping(path = "/{id}") + public ResponseEntity getById(@PathVariable long id) { + return historyService.getById(id) + .map(record -> ResponseEntity.status(HttpStatus.OK).body(record)) + .orElse(ResponseEntity.status(HttpStatus.NOT_FOUND).build()); + } +//update input by specific ID + @PutMapping(path = "/{id}") + public ResponseEntity update(@PathVariable long id, + @RequestBody Map body) { + String newInput = body.get("input"); + return historyService.update(id, newInput) + .map(record -> ResponseEntity.status(HttpStatus.OK).body(record)) + .orElse(ResponseEntity.status(HttpStatus.NOT_FOUND).build()); + } +//Delete all the records + @DeleteMapping + public ResponseEntity clearAll() { + historyService.clearAll(); + return ResponseEntity.status(HttpStatus.OK).body("History has been Deleted"); + } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/controller/MeasurementController.java b/src/main/java/com/oraclequantapi/oraclequantapi/controller/MeasurementController.java new file mode 100644 index 0000000..0ca9bae --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/controller/MeasurementController.java @@ -0,0 +1,24 @@ +package com.oraclequantapi.oraclequantapi.controller; +import jakarta.servlet.http.HttpServletRequest; +import com.oraclequantapi.oraclequantapi.model.MeasurementRecord; +import com.oraclequantapi.oraclequantapi.service.HistoryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +@RestController +@RequestMapping(path = "/convert-measurements") +public class MeasurementController { + + @Autowired + public HistoryService historyService; + + @GetMapping + public ResponseEntity> convertMeasurements(@RequestParam("input") String input, + HttpServletRequest request) { + MeasurementRecord record = historyService.save(input, request.getRemoteAddr()); + return ResponseEntity.status(HttpStatus.OK).body(record.getOutput()); + } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/model/MeasurementRecord.java b/src/main/java/com/oraclequantapi/oraclequantapi/model/MeasurementRecord.java new file mode 100644 index 0000000..686af6b --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/model/MeasurementRecord.java @@ -0,0 +1,45 @@ +package com.oraclequantapi.oraclequantapi.model; +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.util.List; + +@Entity +@Table(name = "measurement_records") +public class MeasurementRecord { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private long id; + + private LocalDateTime timestamp; + private String sourceIpAddress; + private String input; + + @ElementCollection + @CollectionTable(name = "measurement_output", joinColumns = @JoinColumn(name = "record_id")) + @OrderColumn(name = "output_index") + @Column(name = "output_value") + private List output; + + public MeasurementRecord() {} + + public MeasurementRecord(long id, LocalDateTime timestamp, String sourceIpAddress, String input, List output) { + this.id = id; + this.timestamp = timestamp; + this.sourceIpAddress = sourceIpAddress; + this.input = input; + this.output = output; + } + + public long getId() { return id; } + + public LocalDateTime getTimestamp() { return timestamp; } + + public String getSourceIpAddress() { return sourceIpAddress; } + + public String getInput() { return input; } + public void setInput(String input) { this.input = input; } + + public List getOutput() { return output; } + public void setOutput(List output) { this.output = output; } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/repository/MeasurementRecordRepository.java b/src/main/java/com/oraclequantapi/oraclequantapi/repository/MeasurementRecordRepository.java new file mode 100644 index 0000000..e3445ff --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/repository/MeasurementRecordRepository.java @@ -0,0 +1,10 @@ +package com.oraclequantapi.oraclequantapi.repository; + +import com.oraclequantapi.oraclequantapi.model.MeasurementRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MeasurementRecordRepository extends JpaRepository { +} + diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/service/HistoryService.java b/src/main/java/com/oraclequantapi/oraclequantapi/service/HistoryService.java new file mode 100644 index 0000000..c325826 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/service/HistoryService.java @@ -0,0 +1,52 @@ +package com.oraclequantapi.oraclequantapi.service; +import com.oraclequantapi.oraclequantapi.model.MeasurementRecord; +import com.oraclequantapi.oraclequantapi.repository.MeasurementRecordRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Service +public class HistoryService { + + @Autowired + private MeasurementRecordRepository measurementRecordRepository; + + @Autowired + private MeasurementParser measurementParser; + + public MeasurementRecord save(String input, String sourceIp) { + List output = measurementParser.parse(input); + MeasurementRecord record = new MeasurementRecord( + 0, + LocalDateTime.now(), + sourceIp, + input, + output + ); + return measurementRecordRepository.save(record); + } + + public List getAll() { + return measurementRecordRepository.findAll(); + } + + public Optional getById(long id) { + return measurementRecordRepository.findById(id); + } + + public Optional update(long id, String newInput) { + Optional record = measurementRecordRepository.findById(id); + record.ifPresent(r -> { + r.setInput(newInput); + r.setOutput(measurementParser.parse(newInput)); + measurementRecordRepository.save(r); + }); + return record; + } + + public void clearAll() { + measurementRecordRepository.deleteAll(); + } +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/service/MeasurementParser.java b/src/main/java/com/oraclequantapi/oraclequantapi/service/MeasurementParser.java new file mode 100644 index 0000000..ffb6ee6 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/service/MeasurementParser.java @@ -0,0 +1,57 @@ +package com.oraclequantapi.oraclequantapi.service; +import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + +@Service +public class MeasurementParser { + + public List parse(String input) { + List packageTotals = new ArrayList<>(); + int i = 0; + + while (i < input.length()) { + + // Step 1: read cycleCount + int cycleCount = 0; + while (i < input.length()) { + char c = input.charAt(i); + i++; + if (c == 'z') { + cycleCount += 26; + } else { + cycleCount += charValue(c); + break; + } + } + + // Step 2: read values and sum them + long packageTotal = 0; + for (int v = 0; v < cycleCount; v++) { + if (i >= input.length()) break; + + long value = 0; + while (i < input.length()) { + char c = input.charAt(i); + i++; + if (c == 'z') { + value += 26; + } else { + value += charValue(c); + break; + } + } + packageTotal += value; + } + + packageTotals.add(packageTotal); + } + + return packageTotals; + } + + private int charValue(char c) { + if (c == '_') return 0; + return c - 'a' + 1; // a=1, b=2, ..., z=26 + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 99d0060..8240577 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,12 @@ spring.application.name=oraclequantapi + +# Oracle XE datasource +spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XEPDB1 +spring.datasource.username=system +spring.datasource.password=29999login +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + +# JPA / Hibernate +spring.jpa.database-platform=org.hibernate.dialect.OracleDialect +spring.jpa.hibernate.ddl-auto=update +spring.jpa.show-sql=true diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..33dddd7 --- /dev/null +++ b/version.txt @@ -0,0 +1,7 @@ +v0.0.1 - 2026-05-24 +- Added GET /convert-measurements endpoint to parse measurement input string +- Added history tracking with Oracle XE database +- Added GET /history to retrieve all history records +- Added GET /history/{id} to retrieve a specific history record +- Added PUT /history/{id} to update a history record +- Added DELETE /history to clear all history records \ No newline at end of file