diff --git a/pom.xml b/pom.xml
index 5f337c2..6eccd8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,19 +36,32 @@
org.springframework.boot
spring-boot-starter-data-jpa
-
+
org.keycloak
keycloak-spring-boot-starter
24.0.1
-
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
org.postgresql
@@ -89,7 +102,7 @@
com.h2database
h2
test
-
+
diff --git a/src/main/java/it/cnr/isti/workflow/manager/JwtUtil.java b/src/main/java/it/cnr/isti/workflow/manager/JwtUtil.java
new file mode 100644
index 0000000..2ad2637
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/JwtUtil.java
@@ -0,0 +1,52 @@
+package it.cnr.isti.workflow.manager;
+
+import java.util.Date;
+
+import javax.crypto.SecretKey;
+
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
+
+@Component
+public class JwtUtil {
+
+ SecretKey secretKey = Keys.hmacShaKeyFor("la-tua-chiave-segreta-di-almeno-32-byte!".getBytes());
+
+ public String generateToken(String username) {
+ String jwt = Jwts.builder()
+ .setSubject(username)
+ .setIssuedAt(new Date())
+ .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1 giorno
+ .signWith(secretKey, SignatureAlgorithm.HS256) // Usa una SecretKey (HMAC)
+ .compact();
+ return jwt;
+ }
+
+ public String extractUsername(String token) {
+ return Jwts.parserBuilder()
+ .setSigningKey(secretKey)
+ .build()
+ .parseClaimsJws(token)
+ .getBody()
+ .getSubject();
+ }
+
+ public boolean validateToken(String token, LoginEntity userDetails) {
+ final String username = extractUsername(token);
+ return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
+ }
+
+ private boolean isTokenExpired(String token) {
+ Date expiration = Jwts.parserBuilder()
+ .setSigningKey(secretKey)
+ .build()
+ .parseClaimsJws(token)
+ .getBody()
+ .getExpiration();
+ return expiration.before(new Date());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/it/cnr/isti/workflow/manager/TransformerService.java b/src/main/java/it/cnr/isti/workflow/manager/TransformerService.java
index d678784..a7ace77 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/TransformerService.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/TransformerService.java
@@ -16,7 +16,6 @@ import it.cnr.isti.workflow.manager.model.ExecutionStep;
import it.cnr.isti.workflow.manager.model.types.NodeDefinition;
import it.cnr.isti.workflow.manager.model.ui.Connection;
import it.cnr.isti.workflow.manager.model.ui.Flow;
-import it.cnr.isti.workflow.manager.model.ui.IOModel;
import it.cnr.isti.workflow.manager.model.ui.Node;
import it.cnr.isti.workflow.manager.repositories.NodeDefinitionRepository;
diff --git a/src/main/java/it/cnr/isti/workflow/manager/WorkflowManagerApplication.java b/src/main/java/it/cnr/isti/workflow/manager/WorkflowManagerApplication.java
index 8907b7f..4c2a113 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/WorkflowManagerApplication.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/WorkflowManagerApplication.java
@@ -13,6 +13,7 @@ import org.springframework.util.ResourceUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import it.cnr.isti.workflow.manager.executors.ai.AIModel;
+import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
import it.cnr.isti.workflow.manager.model.types.IOType;
import it.cnr.isti.workflow.manager.model.types.Key;
import it.cnr.isti.workflow.manager.model.types.NodeDefinition;
@@ -21,6 +22,7 @@ import it.cnr.isti.workflow.manager.model.types.ParameterType;
import it.cnr.isti.workflow.manager.model.types.Translator;
import it.cnr.isti.workflow.manager.model.ui.Flow;
import it.cnr.isti.workflow.manager.model.ui.FlowEntity;
+import it.cnr.isti.workflow.manager.repositories.AuthRepository;
import it.cnr.isti.workflow.manager.repositories.FlowRepository;
import it.cnr.isti.workflow.manager.repositories.NodeDefinitionRepository;
@@ -36,7 +38,7 @@ public class WorkflowManagerApplication {
@Bean
@ConditionalOnProperty(prefix = "app", name = "db.init.enabled", havingValue = "true")
- CommandLineRunner init(NodeDefinitionRepository repository, FlowRepository flowRepository) {
+ CommandLineRunner init(NodeDefinitionRepository repository, FlowRepository flowRepository, AuthRepository authRepository) {
return args -> {
JSONObject rPars = new JSONObject().put("options",
@@ -69,6 +71,8 @@ public class WorkflowManagerApplication {
loadNodeDefinitionAndSave(repository);
loadFlowsAndSave(flowRepository);
+
+ insertUser(authRepository);
};
}
@@ -101,4 +105,9 @@ public class WorkflowManagerApplication {
}
}
+ private void insertUser(AuthRepository repository) {
+ LoginEntity le = new LoginEntity("lucio", "lucio");
+ repository.save(le);
+ }
+
}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/configurations/JwtAuthenticationFilter.java b/src/main/java/it/cnr/isti/workflow/manager/configurations/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..bae2da5
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/configurations/JwtAuthenticationFilter.java
@@ -0,0 +1,56 @@
+package it.cnr.isti.workflow.manager.configurations;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.NonNull;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import it.cnr.isti.workflow.manager.JwtUtil;
+import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
+import it.cnr.isti.workflow.manager.repositories.AuthRepository;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ @Autowired
+ private JwtUtil jwtUtil;
+
+ @Autowired
+ private AuthRepository authRepository;
+
+ @Override
+ protected void doFilterInternal(@NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain) throws ServletException, IOException {
+ String authHeader = request.getHeader("Authorization");
+
+ if (authHeader != null && authHeader.startsWith("Bearer ")) {
+ String jwt = authHeader.substring(7);
+ String username = jwtUtil.extractUsername(jwt);
+
+ if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+ LoginEntity userDetails = authRepository.findById(username).orElse(null);
+
+ if (jwtUtil.validateToken(jwt, userDetails)) {
+ UsernamePasswordAuthenticationToken authToken =
+ new UsernamePasswordAuthenticationToken(userDetails, null, Collections.emptyList());
+
+ authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authToken);
+ }
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/configurations/SecurityConfig.java b/src/main/java/it/cnr/isti/workflow/manager/configurations/SecurityConfig.java
index 93a4e8f..d4e96fe 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/configurations/SecurityConfig.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/configurations/SecurityConfig.java
@@ -1,22 +1,40 @@
package it.cnr.isti.workflow.manager.configurations;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-//import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-//import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+@Configuration
+@EnableWebSecurity
public class SecurityConfig {
-/*
+
+ @Autowired
+ private JwtAuthenticationFilter jwtFilter;
+
@Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- .authorizeHttpRequests(authz -> authz
- .requestMatchers("/public/**").permitAll()
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+ return http
+ .cors(Customizer.withDefaults())
+ .csrf(csrf -> csrf.disable())
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
- .oauth2ResourceServer(oauth2 -> oauth2.jwt());
-
- return http.build();
+ .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
+ .build();
}
-*/
-}
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
+ return config.getAuthenticationManager();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/it/cnr/isti/workflow/manager/configurations/WebConfig.java b/src/main/java/it/cnr/isti/workflow/manager/configurations/WebConfig.java
index e3fb1e8..d85995d 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/configurations/WebConfig.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/configurations/WebConfig.java
@@ -19,7 +19,6 @@ public class WebConfig implements WebMvcConfigurer {
registry.addMapping("/**")
.allowedOrigins(origins)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
- .allowedHeaders("*")
- .allowCredentials(true);
+ .allowedHeaders("*");
}
}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java b/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java
new file mode 100644
index 0000000..49f827f
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/controllers/AuthController.java
@@ -0,0 +1,59 @@
+package it.cnr.isti.workflow.manager.controllers;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import it.cnr.isti.workflow.manager.JwtUtil;
+import it.cnr.isti.workflow.manager.model.AuthRequest;
+import it.cnr.isti.workflow.manager.model.ChangePasswordRequest;
+import it.cnr.isti.workflow.manager.services.AuthService;
+
+import java.util.Collections;
+
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+
+@RestController
+@RequestMapping("/auth")
+public class AuthController {
+
+ private static final Logger logger = org.slf4j.LoggerFactory.getLogger(AuthController.class);
+
+ @Autowired
+ private JwtUtil jwtUtil;
+
+ @Autowired
+ private AuthService authService;
+
+ @PostMapping("/login")
+ public ResponseEntity> login(@RequestBody AuthRequest request) {
+ logger.info("Login attempt for user: {}", request.getUsername());
+ if (!authService.validateUser(request.getUsername(), request.getPassword())) {
+ return ResponseEntity.status(401).body("Invalid credentials");
+ }
+ String jwt = jwtUtil.generateToken(request.getUsername());
+ return ResponseEntity.ok(Collections.singletonMap("token", jwt));
+
+ }
+
+ @PostMapping("/register")
+ public ResponseEntity> register(@RequestBody AuthRequest request) {
+ if (authService.registerUser(request.getUsername(), request.getPassword())) {
+ return ResponseEntity.ok().build();
+ }
+ return ResponseEntity.badRequest().body("the user already exists");
+ }
+
+ @PostMapping("/change-password")
+ public ResponseEntity> changePassword(@RequestBody ChangePasswordRequest request) {
+ if (authService.changePassword(request.getUsername(), request.getOldPassword(), request.getNewPassword())) {
+ return ResponseEntity.ok().build();
+ }
+ return ResponseEntity.badRequest().body("Change password failed");
+ }
+}
+
diff --git a/src/main/java/it/cnr/isti/workflow/manager/executors/ai/GeminiModel.java b/src/main/java/it/cnr/isti/workflow/manager/executors/ai/GeminiModel.java
index 7501394..79956e4 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/executors/ai/GeminiModel.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/executors/ai/GeminiModel.java
@@ -46,6 +46,7 @@ public class GeminiModel implements AIModel {
// Deserializzazione della risposta JSON in un oggetto GeminiResponse
GeminiResponse response = objectMapper.readValue(result, GeminiResponse.class);
+
// Estrazione del testo dal primo candidato
String candidateText = response.getCandidates().get(0).getContent().getParts().get(0).getText();
diff --git a/src/main/java/it/cnr/isti/workflow/manager/model/AuthRequest.java b/src/main/java/it/cnr/isti/workflow/manager/model/AuthRequest.java
new file mode 100644
index 0000000..109bb32
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/model/AuthRequest.java
@@ -0,0 +1,20 @@
+package it.cnr.isti.workflow.manager.model;
+
+
+public class AuthRequest {
+ private String username;
+ private String password;
+
+ // Getters e Setters
+ public String getUsername() { return username; }
+ public void setUsername(String username) { this.username = username; }
+
+ public String getPassword() { return password; }
+ public void setPassword(String password) { this.password = password; }
+
+ boolean isValid() {
+ return username.contains(" ") && username != null && !username.isEmpty() && username.length()>5 &&
+ password != null && username.contains(" ") && password.length()>5 && !password.isEmpty();
+ }
+}
+
diff --git a/src/main/java/it/cnr/isti/workflow/manager/model/ChangePasswordRequest.java b/src/main/java/it/cnr/isti/workflow/manager/model/ChangePasswordRequest.java
new file mode 100644
index 0000000..2807b38
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/model/ChangePasswordRequest.java
@@ -0,0 +1,18 @@
+package it.cnr.isti.workflow.manager.model;
+
+
+public class ChangePasswordRequest {
+ private String username;
+ private String oldPassword;
+ private String newPassword;
+
+ // Getters e Setters
+ public String getUsername() { return username; }
+ public void setUsername(String username) { this.username = username; }
+
+ public String getOldPassword() { return oldPassword; }
+ public void setOldPassword(String oldPassword) { this.oldPassword = oldPassword; }
+
+ public String getNewPassword() { return newPassword; }
+ public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
+}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/model/ExecutionStep.java b/src/main/java/it/cnr/isti/workflow/manager/model/ExecutionStep.java
index f793c47..686102e 100644
--- a/src/main/java/it/cnr/isti/workflow/manager/model/ExecutionStep.java
+++ b/src/main/java/it/cnr/isti/workflow/manager/model/ExecutionStep.java
@@ -6,6 +6,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import org.slf4j.Logger;
@@ -78,16 +79,25 @@ public class ExecutionStep implements InputReadyListener {
if (areAllInputsReady())
new Thread(() -> {
this.context.getStepsUnderExecution().add(this.id);
+ log.info("starting executing of step {}", this.nodeDefinition.getType());
try {
Map realInputs = prepareInputs();
//log.debug("input translation for step {} is {}", id, realInputs);
Map returned = executor.execute(this.runtimeParameters, null, realInputs);
- this.context.getNodeResult().put(this.id, returned);
- for (InputReadyListener nextStep : nextSteps)
- for (Map.Entry entry : returned.entrySet())
- nextStep.inputReady(!nodeDefinition.getExecutorToOutputTranslationMappings().isEmpty()
- ? nodeDefinition.getExecutorToOutputTranslationMappings().get(entry.getKey())
- : entry.getKey(), entry.getValue());
+
+ //mapping executor output to current node output
+ Map mappedReturn = returned.entrySet().stream().collect(Collectors.toMap(
+ e -> { return !nodeDefinition.getExecutorToOutputTranslationMappings().isEmpty()
+ ? nodeDefinition.getExecutorToOutputTranslationMappings().get(e.getKey())
+ : e.getKey();
+ },
+ e -> e.getValue()));
+
+ context.nodeResult.put(this.id, mappedReturn);
+
+ //sendig output to next steps
+ nextSteps.forEach(n -> mappedReturn.forEach((k, v) -> n.inputReady(k,v)));
+
} catch (ExecutionException e) {
log.error("error during execution of step {}: {}", id, e.getMessage());
this.context.getErrors().add(String.format("[%S] %s", this.id, e.getMessage()));
@@ -95,7 +105,9 @@ public class ExecutionStep implements InputReadyListener {
return;
} finally {
this.context.getStepsUnderExecution().remove(this.id);
- }
+ log.info("finished executing of step {}", this.nodeDefinition.getType());
+ }
+
}).start();
else
log.debug("not all inputs are ready for step {}", id);
diff --git a/src/main/java/it/cnr/isti/workflow/manager/model/entities/LoginEntity.java b/src/main/java/it/cnr/isti/workflow/manager/model/entities/LoginEntity.java
new file mode 100644
index 0000000..9176446
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/model/entities/LoginEntity.java
@@ -0,0 +1,17 @@
+package it.cnr.isti.workflow.manager.model.entities;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class LoginEntity {
+ @Id
+ private String username;
+ private String password;
+}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/repositories/AuthRepository.java b/src/main/java/it/cnr/isti/workflow/manager/repositories/AuthRepository.java
new file mode 100644
index 0000000..1855817
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/repositories/AuthRepository.java
@@ -0,0 +1,12 @@
+package it.cnr.isti.workflow.manager.repositories;
+
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
+
+@Repository
+public interface AuthRepository extends JpaRepository {
+
+}
diff --git a/src/main/java/it/cnr/isti/workflow/manager/services/AuthService.java b/src/main/java/it/cnr/isti/workflow/manager/services/AuthService.java
new file mode 100644
index 0000000..b4f78d2
--- /dev/null
+++ b/src/main/java/it/cnr/isti/workflow/manager/services/AuthService.java
@@ -0,0 +1,47 @@
+package it.cnr.isti.workflow.manager.services;
+
+
+import org.keycloak.adapters.jaas.AbstractKeycloakLoginModule.Auth;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import it.cnr.isti.workflow.manager.model.entities.LoginEntity;
+import it.cnr.isti.workflow.manager.repositories.AuthRepository;
+
+
+@Service
+public class AuthService {
+
+ private static final Logger logger = org.slf4j.LoggerFactory.getLogger(AuthService.class);
+
+ @Autowired
+ AuthRepository authRepository;
+
+ public boolean validateUser(String username, String password) {
+ LoginEntity le = authRepository.findById(username).orElse(null);
+ if (le == null) {
+ return false; // User not found
+ }
+ return le.getPassword().equals(password); // Validate password
+ }
+
+ public boolean registerUser(String username, String password) {
+ if (authRepository.existsById(username)) {
+ return false; // User already exists
+ }
+ authRepository.save(new LoginEntity(username, password));
+ return true;
+ }
+
+ public boolean changePassword(String username, String oldPassword, String newPassword) {
+ return authRepository.findById(username)
+ .filter(user -> user.getPassword().equals(oldPassword))
+ .map(user -> {
+ user.setPassword(newPassword);
+ authRepository.save(user);
+ return true;
+ })
+ .orElse(false);
+ }
+}
diff --git a/src/main/resources/saved-node-type.json b/src/main/resources/saved-node-type.json
index 338bbba..994d98f 100644
--- a/src/main/resources/saved-node-type.json
+++ b/src/main/resources/saved-node-type.json
@@ -256,7 +256,7 @@
"inputToExecutorTranslationMappings": { },
"inputTranslators": {
"prompt": {
- "translation": "Act as a **Senior Software Engineer** specializing in **model-driven development**. Generate clean, maintainable code that:\n1. Implements all validated requirements (from CSV) \n2. Matches the structure of the provided class diagram \n3. Follows the interaction flows from the sequence diagram \n\n**Input:** \n1. requirements (ID, Corrected Requirement, Customer Quote):\n\n${{corrected-requirements}}\n\n2. class diagram (PlantUML) :\n\n${{class-diagram}}\n\n3. sequence diagram (PlantUML) :\n\n\n\n\n**Output Rules:** \n- **Language**: Python\n- **Architecture**: \n - 1:1 mapping of PlantUML classes to code \n - Sequence diagram steps → method logic \n- **Quality**: \n - Type hints (Python)\n - Docstrings referencing requirement IDs (e.g., `# F1`) \n - Appropriate error handling \n- **Format**:\n - Output only executable code, do not add any text, besides comments\n\n**Example Output (Python Calculator):** \n\nfrom enum import Enum, auto\n\nclass OperationType(Enum):\n \"\"\"Supported operations (matches class diagram <>)\"\"\"\n ADD = auto() # F3: Basic arithmetic\n SUBTRACT = auto() # F3\n MULTIPLY = auto() # F3\n DIVIDE = auto() # F3\n\nclass Calculator:\n \"\"\"Core calculator logic (from class diagram)\"\"\"\n def __init__(self):\n self.current_input = 0 # F1: Digit entry\n self.stored_value = None # For multi-step operations\n self.selected_op = None # F3: Operation selection\n \n def calculate(self) -> float:\n \"\"\"Execute the current operation (sequence diagram '=' press)\"\"\"\n if not self.selected_op:\n return self.current_input\n \n # Sequence diagram compute step\n match self.selected_op:\n case OperationType.ADD:\n result = self.stored_value + self.current_input\n case OperationType.SUBTRACT:\n result = self.stored_value - self.current_input\n # ... other operations (F3)\n \n self.current_input = result\n return result\n\nclass InputHandler:\n \"\"\"Manages user input flow (sequence diagram interactions)\"\"\"\n def __init__(self, calculator: Calculator, display: Display):\n self.calc = calculator\n self.display = display\n \n def handle_digit(self, digit: int): # F1\n \"\"\"Process digit press (sequence diagram step 1)\"\"\"\n self.calc.current_input = self.calc.current_input * 10 + digit\n self.display.update(str(self.calc.current_input))\n\n def handle_operator(self, op: OperationType): # F3\n \"\"\"Process +,-,*,/ press (sequence diagram step 2)\"\"\"\n self.calc.stored_value = self.calc.current_input\n self.calc.selected_op = op\n self.calc.current_input = 0\n"
+ "translation": "Act as a **Senior Software Engineer** specializing in **model-driven development**. Generate clean, maintainable code that:\n1. Implements all validated requirements (from CSV) \n2. Matches the structure of the provided class diagram \n3. Follows the interaction flows from the sequence diagram \n\n**Input:** \n1. requirements (ID, Corrected Requirement, Customer Quote):\n\n${{corrected-requirements}}\n\n2. class diagram (PlantUML) :\n\n${{class-diagram}}\n\n3. sequence diagram (PlantUML) :\n\n${{sequence-diagram}}\n\n**Output Rules:** \n- **Language**: Python\n- **Architecture**: \n - 1:1 mapping of PlantUML classes to code \n - Sequence diagram steps → method logic \n- **Quality**: \n - Type hints (Python)\n - Docstrings referencing requirement IDs (e.g., `# F1`) \n - Appropriate error handling \n- **Format**:\n - Output only executable code, do not add any text, besides comments\n\n**Example Output (Python Calculator):** \n\nfrom enum import Enum, auto\n\nclass OperationType(Enum):\n \"\"\"Supported operations (matches class diagram <>)\"\"\"\n ADD = auto() # F3: Basic arithmetic\n SUBTRACT = auto() # F3\n MULTIPLY = auto() # F3\n DIVIDE = auto() # F3\n\nclass Calculator:\n \"\"\"Core calculator logic (from class diagram)\"\"\"\n def __init__(self):\n self.current_input = 0 # F1: Digit entry\n self.stored_value = None # For multi-step operations\n self.selected_op = None # F3: Operation selection\n \n def calculate(self) -> float:\n \"\"\"Execute the current operation (sequence diagram '=' press)\"\"\"\n if not self.selected_op:\n return self.current_input\n \n # Sequence diagram compute step\n match self.selected_op:\n case OperationType.ADD:\n result = self.stored_value + self.current_input\n case OperationType.SUBTRACT:\n result = self.stored_value - self.current_input\n # ... other operations (F3)\n \n self.current_input = result\n return result\n\nclass InputHandler:\n \"\"\"Manages user input flow (sequence diagram interactions)\"\"\"\n def __init__(self, calculator: Calculator, display: Display):\n self.calc = calculator\n self.display = display\n \n def handle_digit(self, digit: int): # F1\n \"\"\"Process digit press (sequence diagram step 1)\"\"\"\n self.calc.current_input = self.calc.current_input * 10 + digit\n self.display.update(str(self.calc.current_input))\n\n def handle_operator(self, op: OperationType): # F3\n \"\"\"Process +,-,*,/ press (sequence diagram step 2)\"\"\"\n self.calc.stored_value = self.calc.current_input\n self.calc.selected_op = op\n self.calc.current_input = 0\n"
}
}
}