DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • MCP Logic: How to Make It 40x Simpler
  • AI Paradigm Shift: Analytics Without SQL
  • Self-Hosted Inference Doesn’t Have to Be a Nightmare: How to Use GPUStack
  • Why Human-in-the-Loop Still Matters in AI-Assisted Coding

Trending

  • Building a Zero-Cost Approval Workflow With AWS Lambda Durable Functions
  • Jakarta EE 12: Entering the Data Age of Enterprise Java
  • RAG Is Not Enough: Advanced Retrieval Architectures Using Vertex AI Search on GCP
  • Mocking Kafka for Local Spring Development
  1. DZone
  2. Data Engineering
  3. AI/ML
  4. GenAI: From Prompt to Production

GenAI: From Prompt to Production

GenAI-Logic is a prompt-driven approach to describing a model and logic using natural language. Create a time tracking system and post to production in less than 2 hours.

By 
Tyler Band user avatar
Tyler Band
·
Mar. 25, 25 · Analysis
Likes (7)
Comment
Save
Tweet
Share
6.1K Views

Join the DZone community and get the full member experience.

Join For Free

The Idea

A project time-tracking system should allow entry of:

  • Clients have one or more projects consisting of one or more tasks.
  • Tasks have timesheet entries, completed by persons entering hours working for a client. 
  • Invoices are created by adding invoice items linked to billable timesheet lines.  
  • Once the invoice is marked as ready, send the information to Kafka for invoice processing.

The Prompt

GenAI Logic is a web page, like many chatbots, that allows the user to enter a prompt. The more details provided, the better the result (although you can iterate to add or change the outcome using the “Iterate” button).  

You want to describe the model and attribute, the logic, and the use cases as fully as possible. Then, simply click “Create Projects” and wait about 90 seconds. 

One note: Under “Advance Settings,” change the data model size to 8 (the number of tables to create). This prevents GenAI from adding more tables that are needed.

Data model size

Plain Text
 
Use these names for tables and attributes:

* Client (id, name, email, phone, total_hours, total_amount, budget_amount, is_over_budget)
* Project ( id, client_id, name, total_project_hours, total_project_amount, project_budget_amount, is_over_budget, is_active)
* Invoice: (id, invoice_date, project_id, invoice_amount, payment_total, invoice_balance, is_paid, is_ready,task_count,completed_task_count)
* InvoiceItem(id, invoice_id, task_id, task_amount, is_completed)
* Task (id, project_id, name, description, total_task_hours_worked, total_task_amount_billed, task_budget_hours, is_over_budget,is_completed)
* Person (id, client_id, name, email, phone, billing_rate, total_hours_entered, total_amount_billed)
* Timesheet (id,task_id, person_id, date_worked, hours_worked, billing_rate, total_amount_billed, is_billable)
* Payment (id, invoice_id, amount, payment_date, notes)


Business Logic

Break down the logic into use cases, starting with timesheets, to describe the business logic. For example: “Copy the person’s billing_rate multiplied by the hours_worked to get the total_amount_billed.” 

Then, modify the use case rule to only perform the calculation if the is_billable flag is set to True (some tasks are non-billable). The sums and counts are rolled up from Task to Project to Client. The is_over_budget flag is determined based on the budget_amount of the Client and Project.

The invoice has an is_ready flag that, when set to true, sends the invoice row to Kafka. This allows a Kafka consumer to initiate the workflow integration in a separate container. Other workflows and integrations can be easily added to the use cases.

Business Rules (Logic)

Plain Text
 
Use LogicBank to enforce business logic.

Use case: Person
Total Hours entered is sum of timesheet hours worked
Total amount billed is total hours entered times billing rate
Billing rate must be greater than 0 and less than 200

Use case: Timesheet
Copy billing rate from Person billing rate
If is_billable then the total amount billed is the billing rate times hours worked 
Hours worked must be greater than 0 and less than 15

Use Case: Task
Total task hours worked is the sum of the Timesheet hours worked
Total task amount billed is the sum of the Timesheet total amount billed
Formula: is Over Budget  when total task hours worked exceeds task budget hours

Use Case: Project
Total project hours is the sum of Task total task hours worked
Total project amount is the sum of Task total amount billed
Formula: is Over Budget when total project amount exceeds project budget amount

Use Case: Client
Total hours is the sum of Project total project hours
Total amount is the sum of Project total project amount
Formula: is Over Budget equals true when total amount exceeds budget amount

Use Case: Invoice
Invoice Amount is the sum of InvoiceItem task amount
Payment total is the sum of Payment amount
Invoice balance is invoice amount less payment total
Formula: is_paid when invoice balance is than or equal to zero
Task Count is count of InvoiceItem 
Task completed count is count of InvoiceItem where is_completed is True
Formula: is ready when Task Count is equal to Task Completed Count
Send invoice to Kafka when is_ready is True

Use Case: InvoiceItem
InvoiceItem task amount is copied from Task total task amount billed
InvoiceItem is_completed is sum of Task is_completed when True


The Project 

When completed, GenAI Logic creates an SQL Database (SQLite), a SQLAlchemy ORM model, an API (JSON API), a react-admin client, complete business logic (25+ rules), and sample data for testing. This would easily be over 1,000 lines of code to handle all the use cases of adding, updating, deleting, and reparenting entries. The entire project can be downloaded from GitHub or a zip file for the developer to review.

Just for fun, the system also created a single-page application landing page to demonstrate your project prompt. 

ProjectTrue


The Data Model

The “Backend Admin” button leads to the working page that includes the logic, the model, a sample react-admin application (for testing the rules), and tools for the developer. This was the model created from the prompt. 

Automatically generated database

Explore the Project

The API entities are now ready to test on the left (a react application) to see test data. Enter a Client, Project, Task, and Timesheet for a Person and watch the sums, counts, and constraint rules change value.

You can also run the generated application locally using Docker or click on the GitHub link to see the code and run the application using GitHub CodeSpaces. 

ProjectTimeTracker 

Finally, explore the API endpoints using OpenAPI (Swagger). If the “> Logic Rules” have any issues, they can be individually reviewed, edited, rejected, or accepted. GenAI can even suggest logic based on your model (see the “Logic” button). 

Note: You can also run the Docker version locally.


The Logic Logs: Auditable and Transparent

Business logic is not a black box. As API endpoints are accessed (POST, PATCH, and DELETE), the rules fire on state changes of dependent entities and attributes. A complete log of rules that fired and the order they fired, along with the row values impacted, gives a full, transparent view of the processing. This is not a black box but a true companion to businesses that require auditable and transparent logic transactions.

Note that the before_flush shows the state change of each row impacted in order, and the after_flush shows the final collection of rules that fired (this is a lot of detail, but it is important if you need to pass an audit). 

Also, note that the order starts with the timesheet change and cascades up to the parent Client. Rules are unordered in the declaration (see logic folder) and can easily be added/modified without impacting other rules.

Plain Text
 
Logic Phase:		ROW LOGIC		(session=0xffffac2f0bf0) (sqlalchemy before_flush)			
..Timesheet[3] {Update - client} id: 3, task_id: 12, person_id: 6, date_worked:  [2025-02-20-->] 2025-02-20 00:00:00, hours_worked:  [12.00-->] 10, billing_rate: 100, total_amount_billed: 1200, is_billable: True  row: 0xffffac2443b0  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
..Timesheet[3] {Formula total_amount_billed} id: 3, task_id: 12, person_id: 6, date_worked:  [2025-02-20-->] 2025-02-20 00:00:00, hours_worked:  [12.00-->] 10, billing_rate: 100, total_amount_billed:  [1200-->] 1000, is_billable: True  row: 0xffffac2443b0  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
....Person[6] {Update - Adjusting person: total_hours_entered} id: 6, client_id: 8, name: Person (New Client), email: [email protected], phone: 8885551212, billing_rate: 100.00, total_hours_entered:  [12.00-->] 10.00, total_amount_billed: 1200.00  row: 0xffffac244380  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
....Person[6] {Formula total_amount_billed} id: 6, client_id: 8, name: Person (New Client), email: [email protected], phone: 8885551212, billing_rate: 100.00, total_hours_entered:  [12.00-->] 10.00, total_amount_billed:  [1200.00-->] 1000.0000  row: 0xffffac244380  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
....Task[12] {Update - Adjusting task: total_task_hours_worked, total_task_amount_billed} id: 12, project_id: 6, name: New Task, description: , total_task_hours_worked:  [12.00-->] 10.00, total_task_amount_billed:  [1200.00-->] 1000.00, task_budget_hours: 1000.00, is_over_budget: False, is_completed: False  row: 0xffffac2450a0  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
......Project[6] {Update - Adjusting project: total_project_hours, total_project_amount} id: 6, client_id: 8, name: New Project, total_project_hours:  [12.00-->] 10.00, total_project_amount:  [1200.00-->] 1000.00, project_budget_amount: 1000.00, is_over_budget: True, is_active: False  row: 0xffffac247860  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
......Project[6] {Formula is_over_budget} id: 6, client_id: 8, name: New Project, total_project_hours:  [12.00-->] 10.00, total_project_amount:  [1200.00-->] 1000.00, project_budget_amount: 1000.00, is_over_budget:  [True-->] False, is_active: False  row: 0xffffac247860  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd
........Client[8] {Update - Adjusting client: total_hours, total_amount} id: 8, name: New Client, email: , phone: , total_hours:  [12.00-->] 10.00, total_amount:  [1200.00-->] 1000.00, budget_amount: 0.00, is_over_budget: True  row: 0xffffac2321e0  session: 0xffffac2f0bf0  ins_upd_dlt: upd, initial: upd

Logic Phase:		COMMIT LOGIC		(session=0xffffac2f0bf0)   				
Logic Phase:		AFTER_FLUSH LOGIC	(session=0xffffac2f0bf0)   										
These Rules Fired (see Logic Phases, above, for actual order):		##
  Client		##
    1. Derive <class 'database.models.Client'>.total_hours as Sum(Project.total_project_hours Where  - None)		##
    2. Derive <class 'database.models.Client'>.total_amount as Sum(Project.total_project_amount Where  - None)		##
    3. Derive <class 'database.models.Client'>.is_over_budget as Formula (1): Rule.formula(derive=Client.is_over_budget, as_exp [...]		##
  Person		##
    4. Derive <class 'database.models.Person'>.total_amount_billed as Formula (1): Rule.formula(derive=Person.total_amount_billed, a [...]		##
    5. Derive <class 'database.models.Person'>.total_hours_entered as Sum(Timesheet.hours_worked Where  - None)
  Project		##
    6. Derive <class 'database.models.Project'>.total_project_amount as Sum(Task.total_task_amount_billed Where  - None)       
    7. Derive <class 'database.models.Project'>.is_over_budget as Formula (1): Rule.formula(derive=Project.is_over_budget, 
       as_ex [...]		##
    8. Derive <class 'database.models.Project'>.total_project_hours as Sum(Task.total_task_hours_worked Where  - None)	  Task		##
    9. Derive <class 'database.models.Task'>.total_task_amount_billed as Sum(Timesheet.total_amount_billed Where  - None)
    10. Derive <class 'database.models.Task'>.is_over_budget as Formula (1): Rule.formula(derive=Task.is_over_budget, 
        as_expre [...]		##
    11. Derive <class 'database.models.Task'>.total_task_hours_worked as Sum(Timesheet.hours_worked Where  - None)
  Timesheet		##
    12. Derive <class 'database.models.Timesheet'>.total_amount_billed as Formula (1): Rule.formula(derive=Timesheet.total_amount_billed [...]		##

Logic Phase:		COMPLETE(session=0xffffac2f0bf0))  


ApiLogicServer: The Developer Journey

The entire generative approach is built on top of the open-source ApiLogicServer (ALS). Once the project is downloaded, it can be edited using VSCode (or your favorite IDE). The platform is built on Python 3.12, SQLAlchemy ORM, and Flask.  

The super-power is the declarative rules engine LogicBank.  

  • Are expressive – 40X the expressive power of procedural code
  • Promote quality – Automatically invoked 
  • Are maintainable – Automatically ordered
  • Rule execution logs – Auditable, traceable, and transparent

The project is organized into folders: API, config, logic, database, integration, security, DevOps, and test. Each folder is thoroughly documented and provides the developer with the ability to add/modify features and services (e.g., custom API endpoints, integrations, behave test cases, or declarative role-based access control) in an IDE. (Bring your own favorite AI tool 0.— CoPilot, Sourcery, Cursor, etc.) 

Here are the steps taken to edit/modify the local project. While it is possible to use SQLIte in production (8 billion smartphones can’t be wrong), I chose the PostgreSQL route.

  1. Convert SQLite to PostgreSQL (sqlite3 database/db.sqlite .dump > timetrack_pg.sql).
  2. Modify the SQL file for PostgreSQL specific (e.g., change sequence to SERIAL8).
  3. Create the PostgreSQL database and rebuild the model ($als rebuild-from-database).
  4. Add KeyCloak security ($als add-auth –provider-type=keycloak or SQL).
  5. Modify the config.py to point to Keycloak or SQL instance.
  6. Add role-based access control for different roles (e.g,. manager, consultant, accounting). See security/declare_security.py.
  7. Initialize the GitHub repository and push changes.
  8. Use devops/docker-image ($sh build-image.sh) to create and push the container to the Docker hub.

Note: You may need to change the linux/amd64 to linux/arm64 for your deployed environment.

Amazon EC2 and RDS Work

  1. Create an RDS PostgreSQL instance and deploy timetrack_pg.sql (create database timetracker).
  2. Create an EC2 instance and pull the docker image into the container.
  3. Add a docker-compose file to start the image and connect to RDS.
  4. Test the authorization http://ec2……compute-1.amazonaws.com:5656.

Test Driven Development

One quick word about testing logic. Using the Behave scenario feature shown below, we quickly added the implementation to insert various API entities and test the derivations, aggregations, and events with full log traceability.

Python
 
  Scenario: New Timesheet
    Given Enter Timesheet Data
      When Timesheet Post
      Then Timesheet entered and balances
      
     
@given('Enter Timesheet Data')
def step_impl(context):
    context.data = {"task_id": task_id, "person_id": person_id, "hours_worked": 10, "date_worked": "2025-01-01","is_billable": True}
    assert True is not False

@when('Timesheet Post')
def step_impl(context):
    print(context.data)
    r = test_utils.post("Timesheet", context.data)
    context.response = r
    assert True is not False

@then('Timesheet entered and balances')
def step_impl(context):
    scenario = "New Timesheet"
    test_utils.prt(f'Rules Report', scenario)
    global timesheet_id 
    timesheet_id = int(context.response["data"]["id"])
    r = test_utils.get("Client", client_id)
    total_hrs = r["data"]["attributes"]["total_hours"] ## 10
    assert total_hrs == 10, f"Total Hours: {total_hrs} expected: 10"
    
 The Log
 Mapped Class[Timesheet] rules:		##   - 2025-02-25 13:49:22,057 - logic_logger - INFO
  Derive <class 'database.models.Timesheet'>.billing_rate as Copy(person.billing_rate)		##   - 2025-02-25 13:49:22,057 - logic_logger - INFO
  Derive <class 'database.models.Timesheet'>.total_amount_billed as Formula (1): Rule.formula(derive=Timesheet.total_amount_billed [...]		##   - 2025-02-25 13:49:22,057 - logic_logger - INFO


Angular Front-End Using OntimizeWeb

ApiLogicServer can also generate an Angular front-end using OntimizeWeb from Imatia. Each API endpoint is used to create pages that can be modified using standard tools or regenerated by modifying the templates (jinja style) used to build page styles (tables, grids, master/detail, treeview, etc.).

From the command line:

Shell
 
$als app-create --app=timetracker
$als app-build --app=timetracker
$cd ui/timetracker
$npm install && npm start


Docker and NGINX

Once the Angular front-end has been approved by the UX team, it is ready to be dockerized and pushed. 

  1. cd ui/timetracker
  2. Review nginx/nginx.conf for production routing
  3. Review src/environments/timetracker.prod.ts routing
  4. $docker build -f Dockerfile -t ${dockerhub}/timetracker --rm 
  5. Check in changes to GitHub

Modify EC2 Compose File

Add the new front-end to the docker-compose.yml file and restart. The new Ontimize application should be running on port 80. Use Keycloak to log in (or add SQL authentication and use a name/password). Once logged in, create a client, a project, task, or person and enter a timesheet (below). Then, see the logs to watch how rules fire.  

TimeTracker log in                  

TreeView List of Clients and Tasks

TreeView List of Clients and Tasks

Summary: Prompt to Production in 2 Hours, Human in the Loop

The prompt to production process required approximately 2 hours to complete. Although “Prompt to Production” is the headline, human involvement was still necessary for tasks such as security configuration (authentication and authorization), docker compose file configuration, and UX/UI fine-tuning. 

Furthermore, Kafka and Workflow integrations and configurations required the expertise of a separate team member. The UX team may want to enhance the look and feel, but this is 100% functional and tested.

See the Project Time Tracker on GitHub repository here.

API Business logic Docker (software) sql AI

Opinions expressed by DZone contributors are their own.

Related

  • MCP Logic: How to Make It 40x Simpler
  • AI Paradigm Shift: Analytics Without SQL
  • Self-Hosted Inference Doesn’t Have to Be a Nightmare: How to Use GPUStack
  • Why Human-in-the-Loop Still Matters in AI-Assisted Coding

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook